]> git.proxmox.com Git - efi-boot-shim.git/blob - sbat.c
Try again with includes
[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 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);
26 return EFI_INVALID_PARAMETER;
27 }
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)) {
34 dprint(L"parse_csv_data failed: %r\n", efi_status);
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;
47 dprint(L"row->n_columns:%lu SBAT_SECTION_COLUMNS:%lu\n",
48 row->n_columns, SBAT_SECTION_COLUMNS);
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') {
56 dprint(L"row[%lu].columns[%lu][0] == '\\000'\n", n, i);
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;
101 err:
102 free_csv_list(&csv);
103 return efi_status;
104 }
105
106 void
107 cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries)
108 {
109 if (!n || !entries)
110 return;
111
112 FreePool(entries);
113 }
114
115 EFI_STATUS
116 verify_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) {
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;
136 }
137 }
138 return EFI_SUCCESS;
139 }
140
141 void
142 cleanup_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
151 if (first == NULL || (uintptr_t)entry < (uintptr_t)first)
152 first = entry;
153
154 list_del(&entry->list);
155 }
156 if (first)
157 FreePool(first);
158 }
159
160 EFI_STATUS
161 verify_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)) {
169 dprint(L"%s variable not present\n", SBAT_VAR_NAME);
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
182 out:
183 dprint(L"finished verifying SBAT data: %r\n", efi_status);
184 return efi_status;
185 }
186
187 EFI_STATUS
188 verify_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
196 EFI_STATUS
197 parse_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
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;
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 }
277 err:
278 free_csv_list(&csv);
279 return efi_status;
280 }
281
282 EFI_STATUS
283 parse_sbat_var(list_t *entries)
284 {
285 UINT8 *data = 0;
286 UINTN datasize;
287 EFI_STATUS efi_status;
288
289 if (!entries) {
290 dprint(L"entries is NULL\n");
291 return EFI_INVALID_PARAMETER;
292 }
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
307 static bool
308 check_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
318 bool
319 preserve_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
326 EFI_STATUS
327 set_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 /*
338 * Always set the SbatLevel UEFI variable if it fails to read.
339 *
340 * Don't try to set the SbatLevel UEFI variable if attributes match
341 * and the signature matches.
342 */
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);
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)) {
360 dprint(L"%s variable delete failed %r\n", SBAT_VAR_NAME,
361 efi_status);
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)) {
370 dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME,
371 efi_status);
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)) {
379 dprint(L"%s read failed %r\n", SBAT_VAR_NAME, efi_status);
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 {
389 dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME);
390 }
391
392 FreePool(sbat);
393
394 return efi_status;
395 }
396
397 // vim:fenc=utf-8:tw=75:noet