]> git.proxmox.com Git - efi-boot-shim.git/blob - sbat.c
New upstream version 15.3
[efi-boot-shim.git] / sbat.c
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
8 EFI_STATUS
9 parse_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
21 if (!section_base || !section_size || !n_entries || !entriesp)
22 return EFI_INVALID_PARAMETER;
23
24 INIT_LIST_HEAD(&csv);
25
26 efi_status =
27 parse_csv_data(section_base, end, SBAT_SECTION_COLUMNS, &csv);
28 if (EFI_ERROR(efi_status)) {
29 return efi_status;
30 }
31
32 n = 0;
33 list_for_each(pos, &csv) {
34 struct csv_row * row;
35 size_t i;
36
37 row = list_entry(pos, struct csv_row, list);
38
39 if (row->n_columns < SBAT_SECTION_COLUMNS) {
40 efi_status = EFI_INVALID_PARAMETER;
41 goto err;
42 }
43
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;
49 goto err;
50 }
51 allocsz += strlen(row->columns[i]) + 1;
52 }
53 n++;
54 }
55
56 strtab = AllocateZeroPool(allocsz);
57 if (!strtab) {
58 efi_status = EFI_OUT_OF_RESOURCES;
59 goto err;
60 }
61
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;
66 n = 0;
67
68 list_for_each(pos, &csv) {
69 struct csv_row * row;
70 size_t i;
71 const char **ptrs[] = {
72 &entry->component_name,
73 &entry->component_generation,
74 &entry->vendor_name,
75 &entry->vendor_package_name,
76 &entry->vendor_version,
77 &entry->vendor_url,
78 };
79
80
81 row = list_entry(pos, struct csv_row, list);
82 for (i = 0; i < row->n_columns; i++) {
83 *(ptrs[i]) = strtab;
84 strtab = stpcpy(strtab, row->columns[i]) + 1;
85 }
86 entries[n] = entry;
87 entry++;
88 n++;
89 }
90 *entriesp = entries;
91 *n_entries = n;
92 err:
93 free_csv_list(&csv);
94 return efi_status;
95 }
96
97 void
98 cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries)
99 {
100 if (!n || !entries)
101 return;
102
103 FreePool(entries);
104 }
105
106 EFI_STATUS
107 verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry)
108 {
109 UINT16 sbat_gen, sbat_var_gen;
110
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);
114
115 /*
116 * atoi returns zero for failed conversion, so essentially
117 * badly parsed component_generation will be treated as zero
118 */
119 sbat_gen = atoi((const char *)entry->component_generation);
120 sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation);
121
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;
127 }
128 }
129 return EFI_SUCCESS;
130 }
131
132 void
133 cleanup_sbat_var(list_t *entries)
134 {
135 list_t *pos = NULL, *tmp = NULL;
136 struct sbat_var_entry *entry;
137 void *first = NULL;
138
139 list_for_each_safe(pos, tmp, entries) {
140 entry = list_entry(pos, struct sbat_var_entry, list);
141
142 if ((uintptr_t)entry < (uintptr_t)first && entry != NULL)
143 first = entry;
144
145 list_del(&entry->list);
146 }
147 if (first)
148 FreePool(first);
149 }
150
151 EFI_STATUS
152 verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries)
153 {
154 unsigned int i;
155 list_t *pos = NULL;
156 EFI_STATUS efi_status = EFI_SUCCESS;
157 struct sbat_var_entry *sbat_var_entry;
158
159 if (list_empty(local_sbat_var)) {
160 dprint(L"SBAT variable not present\n");
161 return EFI_SUCCESS;
162 }
163
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))
169 goto out;
170 }
171 }
172
173 out:
174 dprint(L"finished verifying SBAT data: %r\n", efi_status);
175 return efi_status;
176 }
177
178 EFI_STATUS
179 verify_sbat(size_t n, struct sbat_section_entry **entries)
180 {
181 EFI_STATUS efi_status;
182
183 efi_status = verify_sbat_helper(&sbat_var, n, entries);
184 return efi_status;
185 }
186
187 EFI_STATUS
188 parse_sbat_var_data(list_t *entry_list, UINT8 *data, UINTN datasize)
189 {
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;
195 size_t allocsz = 0;
196 size_t n;
197 char *strtab;
198
199 if (!entry_list|| !data || datasize == 0)
200 return EFI_INVALID_PARAMETER;
201
202 INIT_LIST_HEAD(&csv);
203
204 efi_status = parse_csv_data(start, end, SBAT_VAR_COLUMNS, &csv);
205 if (EFI_ERROR(efi_status)) {
206 return efi_status;
207 }
208
209 n = 0;
210 list_for_each(pos, &csv) {
211 struct csv_row * row;
212 size_t i;
213
214 row = list_entry(pos, struct csv_row, list);
215
216 if (row->n_columns < SBAT_VAR_REQUIRED_COLUMNS) {
217 efi_status = EFI_INVALID_PARAMETER;
218 goto err;
219 }
220
221
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;
227 goto err;
228 }
229 allocsz += strlen(row->columns[i]) + 1;
230 }
231 n++;
232 }
233
234 strtab = AllocateZeroPool(allocsz);
235 if (!strtab) {
236 efi_status = EFI_OUT_OF_RESOURCES;
237 goto err;
238 }
239
240 INIT_LIST_HEAD(entry_list);
241
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;
246 n = 0;
247
248 list_for_each(pos, &csv) {
249 struct csv_row * row;
250 size_t i;
251 const char **ptrs[] = {
252 &entry->component_name,
253 &entry->component_generation,
254 &entry->sbat_datestamp,
255 };
256
257 row = list_entry(pos, struct csv_row, list);
258 for (i = 0; i < row->n_columns; i++) {
259 *(ptrs[i]) = strtab;
260 strtab = stpcpy(strtab, row->columns[i]) + 1;
261 }
262 INIT_LIST_HEAD(&entry->list);
263 list_add_tail(&entry->list, entry_list);
264 entries[n] = entry;
265 entry++;
266 n++;
267 }
268 err:
269 free_csv_list(&csv);
270 return efi_status;
271 }
272
273 EFI_STATUS
274 parse_sbat_var(list_t *entries)
275 {
276 UINT8 *data = 0;
277 UINTN datasize;
278 EFI_STATUS efi_status;
279
280 if (!entries)
281 return EFI_INVALID_PARAMETER;
282
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);
286 return efi_status;
287 }
288
289 /*
290 * We've intentionally made sure there's a NUL byte on all variable
291 * allocations, so use that here.
292 */
293 return parse_sbat_var_data(entries, data, datasize+1);
294 }
295
296 static bool
297 check_sbat_var_attributes(UINT32 attributes)
298 {
299 #ifdef ENABLE_SHIM_DEVEL
300 return attributes == UEFI_VAR_NV_BS_RT;
301 #else
302 return attributes == UEFI_VAR_NV_BS ||
303 attributes == UEFI_VAR_NV_BS_TIMEAUTH;
304 #endif
305 }
306
307 EFI_STATUS
308 set_sbat_uefi_variable(void)
309 {
310 EFI_STATUS efi_status = EFI_SUCCESS;
311 UINT32 attributes = 0;
312
313 UINT8 *sbat = NULL;
314 UINTN sbatsize = 0;
315
316 efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize,
317 SHIM_LOCK_GUID, &attributes);
318 /*
319 * Always set the SBAT UEFI variable if it fails to read.
320 *
321 * Don't try to set the SBAT UEFI variable if attributes match and
322 * the signature matches.
323 */
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);
332 FreePool(sbat);
333 return EFI_SUCCESS;
334 } else {
335 FreePool(sbat);
336
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,
342 attributes, 0, "");
343 if (EFI_ERROR(efi_status)) {
344 dprint(L"SBAT variable delete failed %r\n", efi_status);
345 return efi_status;
346 }
347 }
348
349 /* set variable */
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);
354 return efi_status;
355 }
356
357 /* verify that the expected data is there */
358 efi_status = get_variable(SBAT_VAR_NAME, &sbat, &sbatsize,
359 SHIM_LOCK_GUID);
360 if (EFI_ERROR(efi_status)) {
361 dprint(L"SBAT read failed %r\n", efi_status);
362 return efi_status;
363 }
364
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,
368 strlen(SBAT_VAR));
369 efi_status = EFI_INVALID_PARAMETER;
370 } else {
371 dprint(L"SBAT variable initialization succeeded\n");
372 }
373
374 FreePool(sbat);
375
376 return efi_status;
377 }
378
379 // vim:fenc=utf-8:tw=75:noet