]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/cpio.c
c71c218e4fb4a1d9d15369262a52fc41d88bada5
[thirdparty/systemd.git] / src / boot / efi / cpio.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "cpio.h"
4 #include "measure.h"
5 #include "util.h"
6
7 static char *write_cpio_word(char *p, uint32_t v) {
8 static const char hex[] = "0123456789abcdef";
9
10 assert(p);
11
12 /* Writes a CPIO header 8 character hex value */
13
14 for (UINTN i = 0; i < 8; i++)
15 p[7-i] = hex[(v >> (4 * i)) & 0xF];
16
17 return p + 8;
18 }
19
20 static char *mangle_filename(char *p, const char16_t *f) {
21 char* w;
22
23 assert(p);
24 assert(f);
25
26 /* Basically converts UTF-16 to plain ASCII (note that we filtered non-ASCII filenames beforehand, so
27 * this operation is always safe) */
28
29 for (w = p; *f != 0; f++) {
30 assert(*f <= 0x7fu);
31
32 *(w++) = *f;
33 }
34
35 *(w++) = 0;
36 return w;
37 }
38
39 static char *pad4(char *p, const char *start) {
40 assert(p);
41 assert(start);
42 assert(p >= start);
43
44 /* Appends NUL bytes to 'p', until the address is divisible by 4, when taken relative to 'start' */
45
46 while ((p - start) % 4 != 0)
47 *(p++) = 0;
48
49 return p;
50 }
51
52 static EFI_STATUS pack_cpio_one(
53 const char16_t *fname,
54 const void *contents,
55 UINTN contents_size,
56 const char *target_dir_prefix,
57 uint32_t access_mode,
58 uint32_t *inode_counter,
59 void **cpio_buffer,
60 UINTN *cpio_buffer_size) {
61
62 UINTN l, target_dir_prefix_size, fname_size, q;
63 char *a;
64
65 assert(fname);
66 assert(contents_size || contents_size == 0);
67 assert(target_dir_prefix);
68 assert(inode_counter);
69 assert(cpio_buffer);
70 assert(cpio_buffer_size);
71
72 /* Serializes one file in the cpio format understood by the kernel initrd logic.
73 *
74 * See: https://docs.kernel.org/driver-api/early-userspace/buffer-format.html */
75
76 if (contents_size > UINT32_MAX) /* cpio cannot deal with > 32bit file sizes */
77 return EFI_LOAD_ERROR;
78
79 if (*inode_counter == UINT32_MAX) /* more than 2^32-1 inodes? yikes. cpio doesn't support that either */
80 return EFI_OUT_OF_RESOURCES;
81
82 l = 6 + 13*8 + 1 + 1; /* Fixed CPIO header size, slash separator, and NUL byte after the file name*/
83
84 target_dir_prefix_size = strlen8(target_dir_prefix);
85 if (l > UINTN_MAX - target_dir_prefix_size)
86 return EFI_OUT_OF_RESOURCES;
87 l += target_dir_prefix_size;
88
89 fname_size = strlen16(fname);
90 if (l > UINTN_MAX - fname_size)
91 return EFI_OUT_OF_RESOURCES;
92 l += fname_size; /* append space for file name */
93
94 /* CPIO can't deal with fnames longer than 2^32-1 */
95 if (target_dir_prefix_size + fname_size >= UINT32_MAX)
96 return EFI_OUT_OF_RESOURCES;
97
98 /* Align the whole header to 4 byte size */
99 l = ALIGN4(l);
100 if (l == UINTN_MAX) /* overflow check */
101 return EFI_OUT_OF_RESOURCES;
102
103 /* Align the contents to 4 byte size */
104 q = ALIGN4(contents_size);
105 if (q == UINTN_MAX) /* overflow check */
106 return EFI_OUT_OF_RESOURCES;
107
108 if (l > UINTN_MAX - q) /* overflow check */
109 return EFI_OUT_OF_RESOURCES;
110 l += q; /* Add contents to header */
111
112 if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
113 return EFI_OUT_OF_RESOURCES;
114 a = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
115
116 *cpio_buffer = a;
117 a = (char *) *cpio_buffer + *cpio_buffer_size;
118
119 a = mempcpy(a, "070701", 6); /* magic ID */
120
121 a = write_cpio_word(a, (*inode_counter)++); /* inode */
122 a = write_cpio_word(a, access_mode | 0100000 /* = S_IFREG */); /* mode */
123 a = write_cpio_word(a, 0); /* uid */
124 a = write_cpio_word(a, 0); /* gid */
125 a = write_cpio_word(a, 1); /* nlink */
126
127 /* Note: we don't make any attempt to propagate the mtime here, for two reasons: it's a mess given
128 * that FAT usually is assumed to operate with timezoned timestamps, while UNIX does not. More
129 * importantly though: the modifications times would hamper our goals of providing stable
130 * measurements for the same boots. After all we extend the initrds we generate here into TPM2
131 * PCRs. */
132 a = write_cpio_word(a, 0); /* mtime */
133 a = write_cpio_word(a, contents_size); /* size */
134 a = write_cpio_word(a, 0); /* major(dev) */
135 a = write_cpio_word(a, 0); /* minor(dev) */
136 a = write_cpio_word(a, 0); /* major(rdev) */
137 a = write_cpio_word(a, 0); /* minor(rdev) */
138 a = write_cpio_word(a, target_dir_prefix_size + fname_size + 2); /* fname size */
139 a = write_cpio_word(a, 0); /* "crc" */
140
141 a = mempcpy(a, target_dir_prefix, target_dir_prefix_size);
142 *(a++) = '/';
143 a = mangle_filename(a, fname);
144
145 /* Pad to next multiple of 4 */
146 a = pad4(a, *cpio_buffer);
147
148 a = mempcpy(a, contents, contents_size);
149
150 /* Pad to next multiple of 4 */
151 a = pad4(a, *cpio_buffer);
152
153 assert(a == (char *) *cpio_buffer + *cpio_buffer_size + l);
154 *cpio_buffer_size += l;
155
156 return EFI_SUCCESS;
157 }
158
159 static EFI_STATUS pack_cpio_dir(
160 const char *path,
161 uint32_t access_mode,
162 uint32_t *inode_counter,
163 void **cpio_buffer,
164 UINTN *cpio_buffer_size) {
165
166 UINTN l, path_size;
167 char *a;
168
169 assert(path);
170 assert(inode_counter);
171 assert(cpio_buffer);
172 assert(cpio_buffer_size);
173
174 /* Serializes one directory inode in cpio format. Note that cpio archives must first create the dirs
175 * they want to place files in. */
176
177 if (*inode_counter == UINT32_MAX)
178 return EFI_OUT_OF_RESOURCES;
179
180 l = 6 + 13*8 + 1; /* Fixed CPIO header size, and NUL byte after the file name*/
181
182 path_size = strlen8(path);
183 if (l > UINTN_MAX - path_size)
184 return EFI_OUT_OF_RESOURCES;
185 l += path_size;
186
187 /* Align the whole header to 4 byte size */
188 l = ALIGN4(l);
189 if (l == UINTN_MAX) /* overflow check */
190 return EFI_OUT_OF_RESOURCES;
191
192 if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
193 return EFI_OUT_OF_RESOURCES;
194
195 *cpio_buffer = a = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
196 a = (char *) *cpio_buffer + *cpio_buffer_size;
197
198 a = mempcpy(a, "070701", 6); /* magic ID */
199
200 a = write_cpio_word(a, (*inode_counter)++); /* inode */
201 a = write_cpio_word(a, access_mode | 0040000 /* = S_IFDIR */); /* mode */
202 a = write_cpio_word(a, 0); /* uid */
203 a = write_cpio_word(a, 0); /* gid */
204 a = write_cpio_word(a, 1); /* nlink */
205 a = write_cpio_word(a, 0); /* mtime */
206 a = write_cpio_word(a, 0); /* size */
207 a = write_cpio_word(a, 0); /* major(dev) */
208 a = write_cpio_word(a, 0); /* minor(dev) */
209 a = write_cpio_word(a, 0); /* major(rdev) */
210 a = write_cpio_word(a, 0); /* minor(rdev) */
211 a = write_cpio_word(a, path_size + 1); /* fname size */
212 a = write_cpio_word(a, 0); /* "crc" */
213
214 a = mempcpy(a, path, path_size + 1);
215
216 /* Pad to next multiple of 4 */
217 a = pad4(a, *cpio_buffer);
218
219 assert(a == (char *) *cpio_buffer + *cpio_buffer_size + l);
220
221 *cpio_buffer_size += l;
222 return EFI_SUCCESS;
223 }
224
225 static EFI_STATUS pack_cpio_prefix(
226 const char *path,
227 uint32_t dir_mode,
228 uint32_t *inode_counter,
229 void **cpio_buffer,
230 UINTN *cpio_buffer_size) {
231
232 EFI_STATUS err;
233
234 assert(path);
235 assert(inode_counter);
236 assert(cpio_buffer);
237 assert(cpio_buffer_size);
238
239 /* Serializes directory inodes of all prefix paths of the specified path in cpio format. Note that
240 * (similar to mkdir -p behaviour) all leading paths are created with 0555 access mode, only the
241 * final dir is created with the specified directory access mode. */
242
243 for (const char *p = path;;) {
244 const char *e;
245
246 e = strchr8(p, '/');
247 if (!e)
248 break;
249
250 if (e > p) {
251 _cleanup_free_ char *t = NULL;
252
253 t = xstrndup8(path, e - path);
254 if (!t)
255 return EFI_OUT_OF_RESOURCES;
256
257 err = pack_cpio_dir(t, 0555, inode_counter, cpio_buffer, cpio_buffer_size);
258 if (err != EFI_SUCCESS)
259 return err;
260 }
261
262 p = e + 1;
263 }
264
265 return pack_cpio_dir(path, dir_mode, inode_counter, cpio_buffer, cpio_buffer_size);
266 }
267
268 static EFI_STATUS pack_cpio_trailer(
269 void **cpio_buffer,
270 UINTN *cpio_buffer_size) {
271
272 static const char trailer[] =
273 "070701"
274 "00000000"
275 "00000000"
276 "00000000"
277 "00000000"
278 "00000001"
279 "00000000"
280 "00000000"
281 "00000000"
282 "00000000"
283 "00000000"
284 "00000000"
285 "0000000B"
286 "00000000"
287 "TRAILER!!!\0\0\0"; /* There's a fourth NUL byte appended here, because this is a string */
288
289 /* Generates the cpio trailer record that indicates the end of our initrd cpio archive */
290
291 assert(cpio_buffer);
292 assert(cpio_buffer_size);
293 assert_cc(sizeof(trailer) % 4 == 0);
294
295 *cpio_buffer = xrealloc(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer));
296 memcpy((uint8_t*) *cpio_buffer + *cpio_buffer_size, trailer, sizeof(trailer));
297 *cpio_buffer_size += sizeof(trailer);
298
299 return EFI_SUCCESS;
300 }
301
302 static EFI_STATUS measure_cpio(
303 void *buffer,
304 UINTN buffer_size,
305 const uint32_t tpm_pcr[],
306 UINTN n_tpm_pcr,
307 const char16_t *tpm_description,
308 bool *ret_measured) {
309
310 int measured = -1;
311 EFI_STATUS err;
312
313 assert(buffer || buffer_size == 0);
314 assert(tpm_pcr || n_tpm_pcr == 0);
315
316 for (UINTN i = 0; i < n_tpm_pcr; i++) {
317 bool m;
318
319 if (tpm_pcr[i] == UINT32_MAX) /* Disabled */
320 continue;
321
322 err = tpm_log_event(
323 tpm_pcr[i],
324 POINTER_TO_PHYSICAL_ADDRESS(buffer),
325 buffer_size,
326 tpm_description,
327 &m);
328 if (err != EFI_SUCCESS) {
329 log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
330 measured = false;
331 continue;
332 }
333
334 if (measured != false)
335 measured = m;
336 }
337
338 if (ret_measured)
339 *ret_measured = measured > 0;
340
341 return EFI_SUCCESS;
342 }
343
344 EFI_STATUS pack_cpio(
345 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
346 const char16_t *dropin_dir,
347 const char16_t *match_suffix,
348 const char *target_dir_prefix,
349 uint32_t dir_mode,
350 uint32_t access_mode,
351 const uint32_t tpm_pcr[],
352 UINTN n_tpm_pcr,
353 const char16_t *tpm_description,
354 void **ret_buffer,
355 UINTN *ret_buffer_size,
356 bool *ret_measured) {
357
358 _cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
359 UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
360 _cleanup_free_ char16_t *rel_dropin_dir = NULL;
361 _cleanup_free_ EFI_FILE_INFO *dirent = NULL;
362 _cleanup_(strv_freep) char16_t **items = NULL;
363 _cleanup_free_ void *buffer = NULL;
364 uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
365 EFI_STATUS err;
366
367 assert(loaded_image);
368 assert(target_dir_prefix);
369 assert(tpm_pcr || n_tpm_pcr == 0);
370 assert(ret_buffer);
371 assert(ret_buffer_size);
372
373 if (!loaded_image->DeviceHandle)
374 goto nothing;
375
376 err = open_volume(loaded_image->DeviceHandle, &root);
377 if (err == EFI_UNSUPPORTED)
378 /* Error will be unsupported if the bootloader doesn't implement the file system protocol on
379 * its file handles. */
380 goto nothing;
381 if (err != EFI_SUCCESS)
382 return log_error_status_stall(
383 err, L"Unable to open root directory: %r", err);
384
385 if (!dropin_dir)
386 dropin_dir = rel_dropin_dir = xpool_print(L"%D.extra.d", loaded_image->FilePath);
387
388 err = open_directory(root, dropin_dir, &extra_dir);
389 if (err == EFI_NOT_FOUND)
390 /* No extra subdir, that's totally OK */
391 goto nothing;
392 if (err != EFI_SUCCESS)
393 return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
394
395 for (;;) {
396 _cleanup_free_ char16_t *d = NULL;
397
398 err = readdir_harder(extra_dir, &dirent, &dirent_size);
399 if (err != EFI_SUCCESS)
400 return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
401 if (!dirent) /* End of directory */
402 break;
403
404 if (dirent->FileName[0] == '.')
405 continue;
406 if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
407 continue;
408 if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix))
409 continue;
410 if (!is_ascii(dirent->FileName))
411 continue;
412 if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */
413 continue;
414
415 d = xstrdup16(dirent->FileName);
416
417 if (n_items+2 > n_allocated) {
418 UINTN m;
419
420 /* We allocate 16 entries at a time, as a matter of optimization */
421 if (n_items > (UINTN_MAX / sizeof(uint16_t)) - 16) /* Overflow check, just in case */
422 return log_oom();
423
424 m = n_items + 16;
425 items = xrealloc(items, n_allocated * sizeof(uint16_t *), m * sizeof(uint16_t *));
426 n_allocated = m;
427 }
428
429 items[n_items++] = TAKE_PTR(d);
430 items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
431 }
432
433 if (n_items == 0)
434 /* Empty directory */
435 goto nothing;
436
437 /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
438 * are not dependent on read order) */
439 sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16);
440
441 /* Generate the leading directory inodes right before adding the first files, to the
442 * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
443 err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
444 if (err != EFI_SUCCESS)
445 return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
446
447 for (UINTN i = 0; i < n_items; i++) {
448 _cleanup_free_ char *content = NULL;
449 UINTN contentsize;
450
451 err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
452 if (err != EFI_SUCCESS) {
453 log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err);
454 continue;
455 }
456
457 err = pack_cpio_one(
458 items[i],
459 content, contentsize,
460 target_dir_prefix,
461 access_mode,
462 &inode,
463 &buffer, &buffer_size);
464 if (err != EFI_SUCCESS)
465 return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err);
466 }
467
468 err = pack_cpio_trailer(&buffer, &buffer_size);
469 if (err != EFI_SUCCESS)
470 return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
471
472 err = measure_cpio(buffer, buffer_size, tpm_pcr, n_tpm_pcr, tpm_description, ret_measured);
473 if (err != EFI_SUCCESS)
474 return err;
475
476 *ret_buffer = TAKE_PTR(buffer);
477 *ret_buffer_size = buffer_size;
478
479 return EFI_SUCCESS;
480
481 nothing:
482 *ret_buffer = NULL;
483 *ret_buffer_size = 0;
484
485 if (ret_measured)
486 *ret_measured = n_tpm_pcr > 0;
487
488 return EFI_SUCCESS;
489 }
490
491 EFI_STATUS pack_cpio_literal(
492 const void *data,
493 size_t data_size,
494 const char *target_dir_prefix,
495 const char16_t *target_filename,
496 uint32_t dir_mode,
497 uint32_t access_mode,
498 const uint32_t tpm_pcr[],
499 UINTN n_tpm_pcr,
500 const char16_t *tpm_description,
501 void **ret_buffer,
502 UINTN *ret_buffer_size,
503 bool *ret_measured) {
504
505 uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
506 _cleanup_free_ void *buffer = NULL;
507 UINTN buffer_size;
508 EFI_STATUS err;
509
510 assert(data || data_size == 0);
511 assert(target_dir_prefix);
512 assert(target_filename);
513 assert(tpm_pcr || n_tpm_pcr == 0);
514 assert(ret_buffer);
515 assert(ret_buffer_size);
516
517 /* Generate the leading directory inodes right before adding the first files, to the
518 * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
519
520 err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
521 if (err != EFI_SUCCESS)
522 return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
523
524 err = pack_cpio_one(
525 target_filename,
526 data, data_size,
527 target_dir_prefix,
528 access_mode,
529 &inode,
530 &buffer, &buffer_size);
531 if (err != EFI_SUCCESS)
532 return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", target_filename, err);
533
534 err = pack_cpio_trailer(&buffer, &buffer_size);
535 if (err != EFI_SUCCESS)
536 return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
537
538 err = measure_cpio(buffer, buffer_size, tpm_pcr, n_tpm_pcr, tpm_description, ret_measured);
539 if (err != EFI_SUCCESS)
540 return err;
541
542 *ret_buffer = TAKE_PTR(buffer);
543 *ret_buffer_size = buffer_size;
544
545 return EFI_SUCCESS;
546 }