1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "cpio.h"
4 #include "measure.h"
5 #include "util.h"
6 
write_cpio_word(CHAR8 * p,UINT32 v)7 static CHAR8* write_cpio_word(CHAR8 *p, UINT32 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 
mangle_filename(CHAR8 * p,const CHAR16 * f)20 static CHAR8* mangle_filename(CHAR8 *p, const CHAR16 *f) {
21         CHAR8* 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 
pad4(CHAR8 * p,const CHAR8 * start)39 static CHAR8* pad4(CHAR8 *p, const CHAR8* 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 
pack_cpio_one(const CHAR16 * fname,const void * contents,UINTN contents_size,const CHAR8 * target_dir_prefix,UINT32 access_mode,UINT32 * inode_counter,void ** cpio_buffer,UINTN * cpio_buffer_size)52 static EFI_STATUS pack_cpio_one(
53                 const CHAR16 *fname,
54                 const void *contents,
55                 UINTN contents_size,
56                 const CHAR8 *target_dir_prefix,
57                 UINT32 access_mode,
58                 UINT32 *inode_counter,
59                 void **cpio_buffer,
60                 UINTN *cpio_buffer_size) {
61 
62         UINTN l, target_dir_prefix_size, fname_size, q;
63         CHAR8 *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://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt */
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 = strlena(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 = StrLen(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 = ALIGN_TO(l, 4);
100         if (l == UINTN_MAX) /* overflow check */
101                 return EFI_OUT_OF_RESOURCES;
102 
103         /* Align the contents to 4 byte size */
104         q = ALIGN_TO(contents_size, 4);
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 = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
115 
116         *cpio_buffer = a;
117         a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
118 
119         CopyMem(a, "070701", 6); /* magic ID */
120         a += 6;
121 
122         a = write_cpio_word(a, (*inode_counter)++);                         /* inode */
123         a = write_cpio_word(a, access_mode | 0100000 /* = S_IFREG */);      /* mode */
124         a = write_cpio_word(a, 0);                                          /* uid */
125         a = write_cpio_word(a, 0);                                          /* gid */
126         a = write_cpio_word(a, 1);                                          /* nlink */
127 
128         /* Note: we don't make any attempt to propagate the mtime here, for two reasons: it's a mess given
129          * that FAT usually is assumed to operate with timezoned timestamps, while UNIX does not. More
130          * importantly though: the modifications times would hamper our goals of providing stable
131          * measurements for the same boots. After all we extend the initrds we generate here into TPM2
132          * PCRs. */
133         a = write_cpio_word(a, 0);                                          /* mtime */
134         a = write_cpio_word(a, contents_size);                              /* size */
135         a = write_cpio_word(a, 0);                                          /* major(dev) */
136         a = write_cpio_word(a, 0);                                          /* minor(dev) */
137         a = write_cpio_word(a, 0);                                          /* major(rdev) */
138         a = write_cpio_word(a, 0);                                          /* minor(rdev) */
139         a = write_cpio_word(a, target_dir_prefix_size + fname_size + 2);    /* fname size */
140         a = write_cpio_word(a, 0);                                          /* "crc" */
141 
142         CopyMem(a, target_dir_prefix, target_dir_prefix_size);
143         a += target_dir_prefix_size;
144         *(a++) = '/';
145         a = mangle_filename(a, fname);
146 
147         /* Pad to next multiple of 4 */
148         a = pad4(a, *cpio_buffer);
149 
150         CopyMem(a, contents, contents_size);
151         a += contents_size;
152 
153         /* Pad to next multiple of 4 */
154         a = pad4(a, *cpio_buffer);
155 
156         assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l);
157         *cpio_buffer_size += l;
158 
159         return EFI_SUCCESS;
160 }
161 
pack_cpio_dir(const CHAR8 * path,UINT32 access_mode,UINT32 * inode_counter,void ** cpio_buffer,UINTN * cpio_buffer_size)162 static EFI_STATUS pack_cpio_dir(
163                 const CHAR8 *path,
164                 UINT32 access_mode,
165                 UINT32 *inode_counter,
166                 void **cpio_buffer,
167                 UINTN *cpio_buffer_size) {
168 
169         UINTN l, path_size;
170         CHAR8 *a;
171 
172         assert(path);
173         assert(inode_counter);
174         assert(cpio_buffer);
175         assert(cpio_buffer_size);
176 
177         /* Serializes one directory inode in cpio format. Note that cpio archives must first create the dirs
178          * they want to place files in. */
179 
180         if (*inode_counter == UINT32_MAX)
181                 return EFI_OUT_OF_RESOURCES;
182 
183         l = 6 + 13*8 + 1; /* Fixed CPIO header size, and NUL byte after the file name*/
184 
185         path_size = strlena(path);
186         if (l > UINTN_MAX - path_size)
187                 return EFI_OUT_OF_RESOURCES;
188         l += path_size;
189 
190         /* Align the whole header to 4 byte size */
191         l = ALIGN_TO(l, 4);
192         if (l == UINTN_MAX) /* overflow check */
193                 return EFI_OUT_OF_RESOURCES;
194 
195         if (*cpio_buffer_size > UINTN_MAX - l) /* overflow check */
196                 return EFI_OUT_OF_RESOURCES;
197 
198         *cpio_buffer = a = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + l);
199         a = (CHAR8*) *cpio_buffer + *cpio_buffer_size;
200 
201         CopyMem(a, "070701", 6); /* magic ID */
202         a += 6;
203 
204         a = write_cpio_word(a, (*inode_counter)++);                         /* inode */
205         a = write_cpio_word(a, access_mode | 0040000 /* = S_IFDIR */);      /* mode */
206         a = write_cpio_word(a, 0);                                          /* uid */
207         a = write_cpio_word(a, 0);                                          /* gid */
208         a = write_cpio_word(a, 1);                                          /* nlink */
209         a = write_cpio_word(a, 0);                                          /* mtime */
210         a = write_cpio_word(a, 0);                                          /* size */
211         a = write_cpio_word(a, 0);                                          /* major(dev) */
212         a = write_cpio_word(a, 0);                                          /* minor(dev) */
213         a = write_cpio_word(a, 0);                                          /* major(rdev) */
214         a = write_cpio_word(a, 0);                                          /* minor(rdev) */
215         a = write_cpio_word(a, path_size + 1);                              /* fname size */
216         a = write_cpio_word(a, 0);                                          /* "crc" */
217 
218         CopyMem(a, path, path_size + 1);
219         a += path_size + 1;
220 
221         /* Pad to next multiple of 4 */
222         a = pad4(a, *cpio_buffer);
223 
224         assert(a == (CHAR8*) *cpio_buffer + *cpio_buffer_size + l);
225 
226         *cpio_buffer_size += l;
227         return EFI_SUCCESS;
228 }
229 
pack_cpio_prefix(const CHAR8 * path,UINT32 dir_mode,UINT32 * inode_counter,void ** cpio_buffer,UINTN * cpio_buffer_size)230 static EFI_STATUS pack_cpio_prefix(
231                 const CHAR8 *path,
232                 UINT32 dir_mode,
233                 UINT32 *inode_counter,
234                 void **cpio_buffer,
235                 UINTN *cpio_buffer_size) {
236 
237         EFI_STATUS err;
238 
239         assert(path);
240         assert(inode_counter);
241         assert(cpio_buffer);
242         assert(cpio_buffer_size);
243 
244         /* Serializes directory inodes of all prefix paths of the specified path in cpio format. Note that
245          * (similar to mkdir -p behaviour) all leading paths are created with 0555 access mode, only the
246          * final dir is created with the specified directory access mode. */
247 
248         for (const CHAR8 *p = path;;) {
249                 const CHAR8 *e;
250 
251                 e = strchra(p, '/');
252                 if (!e)
253                         break;
254 
255                 if (e > p) {
256                         _cleanup_freepool_ CHAR8 *t = NULL;
257 
258                         t = xstrndup8(path, e - path);
259                         if (!t)
260                                 return EFI_OUT_OF_RESOURCES;
261 
262                         err = pack_cpio_dir(t, 0555, inode_counter, cpio_buffer, cpio_buffer_size);
263                         if (EFI_ERROR(err))
264                                 return err;
265                 }
266 
267                 p = e + 1;
268         }
269 
270         return pack_cpio_dir(path, dir_mode, inode_counter, cpio_buffer, cpio_buffer_size);
271 }
272 
pack_cpio_trailer(void ** cpio_buffer,UINTN * cpio_buffer_size)273 static EFI_STATUS pack_cpio_trailer(
274                 void **cpio_buffer,
275                 UINTN *cpio_buffer_size) {
276 
277         static const char trailer[] =
278                 "070701"
279                 "00000000"
280                 "00000000"
281                 "00000000"
282                 "00000000"
283                 "00000001"
284                 "00000000"
285                 "00000000"
286                 "00000000"
287                 "00000000"
288                 "00000000"
289                 "00000000"
290                 "0000000B"
291                 "00000000"
292                 "TRAILER!!!\0\0\0"; /* There's a fourth NUL byte appended here, because this is a string */
293 
294         /* Generates the cpio trailer record that indicates the end of our initrd cpio archive */
295 
296         assert(cpio_buffer);
297         assert(cpio_buffer_size);
298         assert_cc(sizeof(trailer) % 4 == 0);
299 
300         *cpio_buffer = xreallocate_pool(*cpio_buffer, *cpio_buffer_size, *cpio_buffer_size + sizeof(trailer));
301         CopyMem((UINT8*) *cpio_buffer + *cpio_buffer_size, trailer, sizeof(trailer));
302         *cpio_buffer_size += sizeof(trailer);
303 
304         return EFI_SUCCESS;
305 }
306 
pack_cpio(EFI_LOADED_IMAGE * loaded_image,const CHAR16 * dropin_dir,const CHAR16 * match_suffix,const CHAR8 * target_dir_prefix,UINT32 dir_mode,UINT32 access_mode,const UINT32 tpm_pcr[],UINTN n_tpm_pcr,const CHAR16 * tpm_description,void ** ret_buffer,UINTN * ret_buffer_size)307 EFI_STATUS pack_cpio(
308                 EFI_LOADED_IMAGE *loaded_image,
309                 const CHAR16 *dropin_dir,
310                 const CHAR16 *match_suffix,
311                 const CHAR8 *target_dir_prefix,
312                 UINT32 dir_mode,
313                 UINT32 access_mode,
314                 const UINT32 tpm_pcr[],
315                 UINTN n_tpm_pcr,
316                 const CHAR16 *tpm_description,
317                 void **ret_buffer,
318                 UINTN *ret_buffer_size) {
319 
320         _cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
321         UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
322         _cleanup_freepool_ CHAR16 *rel_dropin_dir = NULL;
323         _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL;
324         _cleanup_(strv_freep) CHAR16 **items = NULL;
325         _cleanup_freepool_ void *buffer = NULL;
326         UINT32 inode = 1; /* inode counter, so that each item gets a new inode */
327         EFI_STATUS err;
328         EFI_FILE_IO_INTERFACE *volume;
329 
330         assert(loaded_image);
331         assert(target_dir_prefix);
332         assert(tpm_pcr || n_tpm_pcr == 0);
333         assert(ret_buffer);
334         assert(ret_buffer_size);
335 
336         if (!loaded_image->DeviceHandle) {
337                 *ret_buffer = NULL;
338                 *ret_buffer_size = 0;
339                 return EFI_SUCCESS;
340         }
341 
342         err = BS->HandleProtocol(loaded_image->DeviceHandle,
343                                  &FileSystemProtocol, (void*)&volume);
344         /* Error will be unsupported if the bootloader doesn't implement the
345          * file system protocol on its file handles.
346          */
347         if (err == EFI_UNSUPPORTED) {
348                 *ret_buffer = NULL;
349                 *ret_buffer_size = 0;
350                 return EFI_SUCCESS;
351         }
352         if (EFI_ERROR(err))
353                 return log_error_status_stall(
354                                 err, L"Unable to load file system protocol: %r", err);
355 
356         err = volume->OpenVolume(volume, &root);
357         if (EFI_ERROR(err))
358                 return log_error_status_stall(
359                                 err, L"Unable to open root directory: %r", err);
360 
361         if (!dropin_dir)
362                 dropin_dir = rel_dropin_dir = xpool_print(L"%D.extra.d", loaded_image->FilePath);
363 
364         err = open_directory(root, dropin_dir, &extra_dir);
365         if (err == EFI_NOT_FOUND) {
366                 /* No extra subdir, that's totally OK */
367                 *ret_buffer = NULL;
368                 *ret_buffer_size = 0;
369                 return EFI_SUCCESS;
370         }
371         if (EFI_ERROR(err))
372                 return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
373 
374         for (;;) {
375                 _cleanup_freepool_ CHAR16 *d = NULL;
376 
377                 err = readdir_harder(extra_dir, &dirent, &dirent_size);
378                 if (EFI_ERROR(err))
379                         return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
380                 if (!dirent) /* End of directory */
381                         break;
382 
383                 if (dirent->FileName[0] == '.')
384                         continue;
385                 if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
386                         continue;
387                 if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix))
388                         continue;
389                 if (!is_ascii(dirent->FileName))
390                         continue;
391                 if (StrLen(dirent->FileName) > 255) /* Max filename size on Linux */
392                         continue;
393 
394                 d = xstrdup(dirent->FileName);
395 
396                 if (n_items+2 > n_allocated) {
397                         UINTN m;
398 
399                         /* We allocate 16 entries at a time, as a matter of optimization */
400                         if (n_items > (UINTN_MAX / sizeof(UINT16)) - 16) /* Overflow check, just in case */
401                                 return log_oom();
402 
403                         m = n_items + 16;
404                         items = xreallocate_pool(items, n_allocated * sizeof(UINT16*), m * sizeof(UINT16*));
405                         n_allocated = m;
406                 }
407 
408                 items[n_items++] = TAKE_PTR(d);
409                 items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
410         }
411 
412         if (n_items == 0) {
413                 /* Empty directory */
414                 *ret_buffer = NULL;
415                 *ret_buffer_size = 0;
416                 return EFI_SUCCESS;
417         }
418 
419         /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
420          * are not dependent on read order) */
421         sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) StrCmp);
422 
423         /* Generate the leading directory inodes right before adding the first files, to the
424          * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
425         err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
426         if (EFI_ERROR(err))
427                 return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
428 
429         for (UINTN i = 0; i < n_items; i++) {
430                 _cleanup_freepool_ CHAR8 *content = NULL;
431                 UINTN contentsize;
432 
433                 err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
434                 if (EFI_ERROR(err)) {
435                         log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err);
436                         continue;
437                 }
438 
439                 err = pack_cpio_one(
440                                 items[i],
441                                 content, contentsize,
442                                 target_dir_prefix,
443                                 access_mode,
444                                 &inode,
445                                 &buffer, &buffer_size);
446                 if (EFI_ERROR(err))
447                         return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err);
448         }
449 
450         err = pack_cpio_trailer(&buffer, &buffer_size);
451         if (EFI_ERROR(err))
452                 return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
453 
454         for (UINTN i = 0; i < n_tpm_pcr; i++) {
455                 err = tpm_log_event(
456                                 tpm_pcr[i],
457                                 POINTER_TO_PHYSICAL_ADDRESS(buffer),
458                                 buffer_size,
459                                 tpm_description);
460                 if (EFI_ERROR(err))
461                         log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
462         }
463 
464         *ret_buffer = TAKE_PTR(buffer);
465         *ret_buffer_size = buffer_size;
466 
467         return EFI_SUCCESS;
468 }
469