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 return EFI_INVALID_PARAMETER
;
27 parse_csv_data(section_base
, end
, SBAT_SECTION_COLUMNS
, &csv
);
28 if (EFI_ERROR(efi_status
)) {
33 list_for_each(pos
, &csv
) {
37 row
= list_entry(pos
, struct csv_row
, list
);
39 if (row
->n_columns
< SBAT_SECTION_COLUMNS
) {
40 efi_status
= EFI_INVALID_PARAMETER
;
44 allocsz
+= sizeof(struct sbat_section_entry
*);
45 allocsz
+= sizeof(struct sbat_section_entry
);
46 for (i
= 0; i
< row
->n_columns
; i
++) {
47 if (row
->columns
[i
][0] == '\000') {
48 efi_status
= EFI_INVALID_PARAMETER
;
51 allocsz
+= strlen(row
->columns
[i
]) + 1;
56 strtab
= AllocateZeroPool(allocsz
);
58 efi_status
= EFI_OUT_OF_RESOURCES
;
62 entries
= (struct sbat_section_entry
**)strtab
;
63 strtab
+= sizeof(struct sbat_section_entry
*) * n
;
64 entry
= (struct sbat_section_entry
*)strtab
;
65 strtab
+= sizeof(struct sbat_section_entry
) * n
;
68 list_for_each(pos
, &csv
) {
71 const char **ptrs
[] = {
72 &entry
->component_name
,
73 &entry
->component_generation
,
75 &entry
->vendor_package_name
,
76 &entry
->vendor_version
,
81 row
= list_entry(pos
, struct csv_row
, list
);
82 for (i
= 0; i
< row
->n_columns
; i
++) {
84 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
98 cleanup_sbat_section_entries(size_t n
, struct sbat_section_entry
**entries
)
107 verify_single_entry(struct sbat_section_entry
*entry
, struct sbat_var_entry
*sbat_var_entry
)
109 UINT16 sbat_gen
, sbat_var_gen
;
111 if (strcmp((const char *)entry
->component_name
, (const char *)sbat_var_entry
->component_name
) == 0) {
112 dprint(L
"component %a has a matching SBAT variable entry, verifying\n",
113 entry
->component_name
);
116 * atoi returns zero for failed conversion, so essentially
117 * badly parsed component_generation will be treated as zero
119 sbat_gen
= atoi((const char *)entry
->component_generation
);
120 sbat_var_gen
= atoi((const char *)sbat_var_entry
->component_generation
);
122 if (sbat_gen
< sbat_var_gen
) {
123 dprint(L
"component %a, generation %d, was revoked by SBAT variable",
124 entry
->component_name
, sbat_gen
);
125 LogError(L
"image did not pass SBAT verification\n");
126 return EFI_SECURITY_VIOLATION
;
133 cleanup_sbat_var(list_t
*entries
)
135 list_t
*pos
= NULL
, *tmp
= NULL
;
136 struct sbat_var_entry
*entry
;
139 list_for_each_safe(pos
, tmp
, entries
) {
140 entry
= list_entry(pos
, struct sbat_var_entry
, list
);
142 if ((uintptr_t)entry
< (uintptr_t)first
&& entry
!= NULL
)
145 list_del(&entry
->list
);
152 verify_sbat_helper(list_t
*local_sbat_var
, size_t n
, struct sbat_section_entry
**entries
)
156 EFI_STATUS efi_status
= EFI_SUCCESS
;
157 struct sbat_var_entry
*sbat_var_entry
;
159 if (list_empty(local_sbat_var
)) {
160 dprint(L
"SBAT variable not present\n");
164 for (i
= 0; i
< n
; i
++) {
165 list_for_each(pos
, local_sbat_var
) {
166 sbat_var_entry
= list_entry(pos
, struct sbat_var_entry
, list
);
167 efi_status
= verify_single_entry(entries
[i
], sbat_var_entry
);
168 if (EFI_ERROR(efi_status
))
174 dprint(L
"finished verifying SBAT data: %r\n", efi_status
);
179 verify_sbat(size_t n
, struct sbat_section_entry
**entries
)
181 EFI_STATUS efi_status
;
183 efi_status
= verify_sbat_helper(&sbat_var
, n
, entries
);
188 parse_sbat_var_data(list_t
*entry_list
, UINT8
*data
, UINTN datasize
)
190 struct sbat_var_entry
*entry
= NULL
, **entries
;
191 EFI_STATUS efi_status
= EFI_SUCCESS
;
192 list_t csv
, *pos
= NULL
;
193 char * start
= (char *)data
;
194 char * end
= (char *)data
+ datasize
- 1;
199 if (!entry_list
|| !data
|| datasize
== 0)
200 return EFI_INVALID_PARAMETER
;
202 INIT_LIST_HEAD(&csv
);
204 efi_status
= parse_csv_data(start
, end
, SBAT_VAR_COLUMNS
, &csv
);
205 if (EFI_ERROR(efi_status
)) {
210 list_for_each(pos
, &csv
) {
211 struct csv_row
* row
;
214 row
= list_entry(pos
, struct csv_row
, list
);
216 if (row
->n_columns
< SBAT_VAR_REQUIRED_COLUMNS
) {
217 efi_status
= EFI_INVALID_PARAMETER
;
222 allocsz
+= sizeof(struct sbat_var_entry
*);
223 allocsz
+= sizeof(struct sbat_var_entry
);
224 for (i
= 0; i
< row
->n_columns
; i
++) {
225 if (!row
->columns
[i
][0]) {
226 efi_status
= EFI_INVALID_PARAMETER
;
229 allocsz
+= strlen(row
->columns
[i
]) + 1;
234 strtab
= AllocateZeroPool(allocsz
);
236 efi_status
= EFI_OUT_OF_RESOURCES
;
240 INIT_LIST_HEAD(entry_list
);
242 entries
= (struct sbat_var_entry
**)strtab
;
243 strtab
+= sizeof(struct sbat_var_entry
*) * n
;
244 entry
= (struct sbat_var_entry
*)strtab
;
245 strtab
+= sizeof(struct sbat_var_entry
) * n
;
248 list_for_each(pos
, &csv
) {
249 struct csv_row
* row
;
251 const char **ptrs
[] = {
252 &entry
->component_name
,
253 &entry
->component_generation
,
254 &entry
->sbat_datestamp
,
257 row
= list_entry(pos
, struct csv_row
, list
);
258 for (i
= 0; i
< row
->n_columns
; i
++) {
260 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
262 INIT_LIST_HEAD(&entry
->list
);
263 list_add_tail(&entry
->list
, entry_list
);
274 parse_sbat_var(list_t
*entries
)
278 EFI_STATUS efi_status
;
281 return EFI_INVALID_PARAMETER
;
283 efi_status
= get_variable(SBAT_VAR_NAME
, &data
, &datasize
, SHIM_LOCK_GUID
);
284 if (EFI_ERROR(efi_status
)) {
285 LogError(L
"Failed to read SBAT variable\n", efi_status
);
290 * We've intentionally made sure there's a NUL byte on all variable
291 * allocations, so use that here.
293 return parse_sbat_var_data(entries
, data
, datasize
+1);
297 check_sbat_var_attributes(UINT32 attributes
)
299 #ifdef ENABLE_SHIM_DEVEL
300 return attributes
== UEFI_VAR_NV_BS_RT
;
302 return attributes
== UEFI_VAR_NV_BS
||
303 attributes
== UEFI_VAR_NV_BS_TIMEAUTH
;
308 set_sbat_uefi_variable(void)
310 EFI_STATUS efi_status
= EFI_SUCCESS
;
311 UINT32 attributes
= 0;
316 efi_status
= get_variable_attr(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
317 SHIM_LOCK_GUID
, &attributes
);
319 * Always set the SBAT UEFI variable if it fails to read.
321 * Don't try to set the SBAT UEFI variable if attributes match and
322 * the signature matches.
324 if (EFI_ERROR(efi_status
)) {
325 dprint(L
"SBAT read failed %r\n", efi_status
);
326 } else if (check_sbat_var_attributes(attributes
) &&
327 sbatsize
>= strlen(SBAT_VAR_SIG
"1") &&
328 strncmp((const char *)sbat
, SBAT_VAR_SIG
,
329 strlen(SBAT_VAR_SIG
))) {
330 dprint("SBAT variable is %d bytes, attributes are 0x%08x\n",
331 sbatsize
, attributes
);
337 /* delete previous variable */
338 dprint("%s variable is %d bytes, attributes are 0x%08x\n",
339 SBAT_VAR_NAME
, sbatsize
, attributes
);
340 dprint("Deleting %s variable.\n", SBAT_VAR_NAME
);
341 efi_status
= set_variable(SBAT_VAR_NAME
, SHIM_LOCK_GUID
,
343 if (EFI_ERROR(efi_status
)) {
344 dprint(L
"SBAT variable delete failed %r\n", efi_status
);
350 efi_status
= set_variable(SBAT_VAR_NAME
, SHIM_LOCK_GUID
, SBAT_VAR_ATTRS
,
351 sizeof(SBAT_VAR
)-1, SBAT_VAR
);
352 if (EFI_ERROR(efi_status
)) {
353 dprint(L
"SBAT variable writing failed %r\n", efi_status
);
357 /* verify that the expected data is there */
358 efi_status
= get_variable(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
360 if (EFI_ERROR(efi_status
)) {
361 dprint(L
"SBAT read failed %r\n", efi_status
);
365 if (sbatsize
!= strlen(SBAT_VAR
) ||
366 strncmp((const char *)sbat
, SBAT_VAR
, strlen(SBAT_VAR
)) != 0) {
367 dprint("new sbatsize is %d, expected %d\n", sbatsize
,
369 efi_status
= EFI_INVALID_PARAMETER
;
371 dprint(L
"SBAT variable initialization succeeded\n");
379 // vim:fenc=utf-8:tw=75:noet