]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
0fa2cac4 | 2 | |
845707aa | 3 | #include "cpio.h" |
e6d7dc9a | 4 | #include "device-path-util.h" |
33bc9b75 | 5 | #include "devicetree.h" |
37fa3690 | 6 | #include "graphics.h" |
0fa2cac4 | 7 | #include "linux.h" |
d4cbada2 | 8 | #include "measure.h" |
0a1d8ac7 | 9 | #include "part-discovery.h" |
d4cbada2 | 10 | #include "pe.h" |
5080a60a | 11 | #include "proto/shell-parameters.h" |
0a1d8ac7 | 12 | #include "random-seed.h" |
ce0f078f | 13 | #include "secure-boot.h" |
05c9f9c2 | 14 | #include "shim.h" |
cf0fbc49 | 15 | #include "splash.h" |
aa323c09 | 16 | #include "tpm-pcr.h" |
cf0fbc49 | 17 | #include "util.h" |
2afeaf16 | 18 | #include "version.h" |
717af0de | 19 | #include "vmm.h" |
0fa2cac4 KS |
20 | |
21 | /* magic string to find in the binary image */ | |
2ccd5986 | 22 | _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####"; |
0fa2cac4 | 23 | |
845707aa | 24 | static EFI_STATUS combine_initrd( |
dede50a7 | 25 | EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size, |
23002b45 | 26 | const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds, |
dede50a7 | 27 | Pages *ret_initr_pages, size_t *ret_initrd_size) { |
845707aa | 28 | |
dede50a7 | 29 | size_t n; |
845707aa | 30 | |
09173c91 | 31 | assert(ret_initr_pages); |
845707aa LP |
32 | assert(ret_initrd_size); |
33 | ||
f3b6f333 | 34 | /* Combines four initrds into one, by simple concatenation in memory */ |
845707aa | 35 | |
b4e7df4a | 36 | n = ALIGN4(initrd_size); /* main initrd might not be padded yet */ |
845707aa | 37 | |
23002b45 LP |
38 | for (size_t i = 0; i < n_extra_initrds; i++) { |
39 | if (!extra_initrds[i]) | |
40 | continue; | |
f3b6f333 | 41 | |
dede50a7 | 42 | if (n > SIZE_MAX - extra_initrd_sizes[i]) |
845707aa LP |
43 | return EFI_OUT_OF_RESOURCES; |
44 | ||
23002b45 | 45 | n += extra_initrd_sizes[i]; |
845707aa LP |
46 | } |
47 | ||
09173c91 | 48 | _cleanup_pages_ Pages pages = xmalloc_pages( |
845707aa LP |
49 | AllocateMaxAddress, |
50 | EfiLoaderData, | |
51 | EFI_SIZE_TO_PAGES(n), | |
09173c91 JJ |
52 | UINT32_MAX /* Below 4G boundary. */); |
53 | uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr); | |
845707aa | 54 | if (initrd_base != 0) { |
dede50a7 | 55 | size_t pad; |
845707aa LP |
56 | |
57 | /* Order matters, the real initrd must come first, since it might include microcode updates | |
58 | * which the kernel only looks for in the first cpio archive */ | |
515581d6 | 59 | p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); |
845707aa | 60 | |
b4e7df4a | 61 | pad = ALIGN4(initrd_size) - initrd_size; |
845707aa | 62 | if (pad > 0) { |
bbc1f2ea | 63 | memset(p, 0, pad); |
845707aa LP |
64 | p += pad; |
65 | } | |
66 | } | |
67 | ||
23002b45 LP |
68 | for (size_t i = 0; i < n_extra_initrds; i++) { |
69 | if (!extra_initrds[i]) | |
70 | continue; | |
71 | ||
72 | p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]); | |
73 | } | |
845707aa | 74 | |
09173c91 | 75 | assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p); |
845707aa | 76 | |
09173c91 | 77 | *ret_initr_pages = pages; |
845707aa | 78 | *ret_initrd_size = n; |
09173c91 | 79 | pages.n_pages = 0; |
845707aa LP |
80 | |
81 | return EFI_SUCCESS; | |
82 | } | |
83 | ||
b30a43df | 84 | static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { |
46d33672 LP |
85 | static const uint64_t stub_features = |
86 | EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */ | |
87 | EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */ | |
88 | EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */ | |
89 | EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */ | |
0a1d8ac7 | 90 | EFI_STUB_FEATURE_RANDOM_SEED | /* We pass a random seed to the kernel */ |
92bb46c4 LP |
91 | EFI_STUB_FEATURE_CMDLINE_ADDONS | /* We pick up .cmdline addons */ |
92 | EFI_STUB_FEATURE_CMDLINE_SMBIOS | /* We support extending kernel cmdline from SMBIOS Type #11 */ | |
46d33672 LP |
93 | 0; |
94 | ||
5a186322 LP |
95 | assert(loaded_image); |
96 | ||
97 | /* Export the device path this image is started from, if it's not set yet */ | |
8e760b3f JJ |
98 | if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) { |
99 | _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle); | |
100 | if (uuid) | |
19f08504 | 101 | efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); |
8e760b3f | 102 | } |
5a186322 LP |
103 | |
104 | /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the | |
105 | * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note | |
106 | * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us, | |
107 | * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong | |
108 | * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath | |
109 | * is non-NULL explicitly.) */ | |
19f08504 | 110 | if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS && |
5a186322 | 111 | loaded_image->FilePath) { |
3639d1b0 | 112 | _cleanup_free_ char16_t *s = NULL; |
616a80fe | 113 | if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS) |
19f08504 | 114 | efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0); |
5a186322 LP |
115 | } |
116 | ||
117 | /* if LoaderFirmwareInfo is not set, let's set it */ | |
19f08504 | 118 | if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { |
3639d1b0 | 119 | _cleanup_free_ char16_t *s = NULL; |
2f3c3b0b | 120 | s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); |
19f08504 | 121 | efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0); |
5a186322 LP |
122 | } |
123 | ||
124 | /* ditto for LoaderFirmwareType */ | |
19f08504 | 125 | if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { |
3639d1b0 | 126 | _cleanup_free_ char16_t *s = NULL; |
2f3c3b0b | 127 | s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); |
19f08504 | 128 | efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); |
5a186322 LP |
129 | } |
130 | ||
46d33672 | 131 | |
24120e40 LP |
132 | /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our |
133 | * own data) */ | |
19f08504 | 134 | (void) efivar_set(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); |
46d33672 | 135 | |
19f08504 | 136 | (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"StubFeatures", stub_features, 0); |
5a186322 LP |
137 | } |
138 | ||
b17f3b3d JJ |
139 | static bool use_load_options( |
140 | EFI_HANDLE stub_image, | |
141 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image, | |
142 | bool have_cmdline, | |
143 | char16_t **ret) { | |
144 | ||
145 | assert(stub_image); | |
146 | assert(loaded_image); | |
147 | assert(ret); | |
148 | ||
149 | /* We only allow custom command lines if we aren't in secure boot or if no cmdline was baked into | |
fab0eeb7 EGE |
150 | * the stub image. |
151 | * We also don't allow it if we are in confidential vms and secureboot is on. */ | |
152 | if (secure_boot_enabled() && (have_cmdline || is_confidential_vm())) | |
b17f3b3d JJ |
153 | return false; |
154 | ||
155 | /* We also do a superficial check whether first character of passed command line | |
156 | * is printable character (for compat with some Dell systems which fill in garbage?). */ | |
157 | if (loaded_image->LoadOptionsSize < sizeof(char16_t) || ((char16_t *) loaded_image->LoadOptions)[0] <= 0x1F) | |
158 | return false; | |
159 | ||
160 | /* The UEFI shell registers EFI_SHELL_PARAMETERS_PROTOCOL onto images it runs. This lets us know that | |
161 | * LoadOptions starts with the stub binary path which we want to strip off. */ | |
162 | EFI_SHELL_PARAMETERS_PROTOCOL *shell; | |
19f08504 | 163 | if (BS->HandleProtocol(stub_image, MAKE_GUID_PTR(EFI_SHELL_PARAMETERS_PROTOCOL), (void **) &shell) |
b17f3b3d JJ |
164 | != EFI_SUCCESS) { |
165 | /* Not running from EFI shell, use entire LoadOptions. Note that LoadOptions is a void*, so | |
166 | * it could be anything! */ | |
167 | *ret = xstrndup16(loaded_image->LoadOptions, loaded_image->LoadOptionsSize / sizeof(char16_t)); | |
168 | mangle_stub_cmdline(*ret); | |
169 | return true; | |
170 | } | |
171 | ||
172 | if (shell->Argc < 2) | |
173 | /* No arguments were provided? Then we fall back to built-in cmdline. */ | |
174 | return false; | |
175 | ||
176 | /* Assemble the command line ourselves without our stub path. */ | |
177 | *ret = xstrdup16(shell->Argv[1]); | |
178 | for (size_t i = 2; i < shell->Argc; i++) { | |
179 | _cleanup_free_ char16_t *old = *ret; | |
2f3c3b0b | 180 | *ret = xasprintf("%ls %ls", old, shell->Argv[i]); |
b17f3b3d JJ |
181 | } |
182 | ||
183 | mangle_stub_cmdline(*ret); | |
184 | return true; | |
185 | } | |
186 | ||
05c9f9c2 LB |
187 | static EFI_STATUS load_addons_from_dir( |
188 | EFI_FILE *root, | |
189 | const char16_t *prefix, | |
190 | char16_t ***items, | |
191 | size_t *n_items, | |
192 | size_t *n_allocated) { | |
193 | ||
194 | _cleanup_(file_closep) EFI_FILE *extra_dir = NULL; | |
195 | _cleanup_free_ EFI_FILE_INFO *dirent = NULL; | |
196 | size_t dirent_size = 0; | |
197 | EFI_STATUS err; | |
198 | ||
199 | assert(root); | |
200 | assert(prefix); | |
201 | assert(items); | |
202 | assert(n_items); | |
203 | assert(n_allocated); | |
204 | ||
205 | err = open_directory(root, prefix, &extra_dir); | |
206 | if (err == EFI_NOT_FOUND) | |
207 | /* No extra subdir, that's totally OK */ | |
208 | return EFI_SUCCESS; | |
209 | if (err != EFI_SUCCESS) | |
210 | return log_error_status(err, "Failed to open addons directory '%ls': %m", prefix); | |
211 | ||
212 | for (;;) { | |
213 | _cleanup_free_ char16_t *d = NULL; | |
214 | ||
215 | err = readdir(extra_dir, &dirent, &dirent_size); | |
216 | if (err != EFI_SUCCESS) | |
217 | return log_error_status(err, "Failed to read addons directory of loaded image: %m"); | |
218 | if (!dirent) /* End of directory */ | |
219 | break; | |
220 | ||
221 | if (dirent->FileName[0] == '.') | |
222 | continue; | |
223 | if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY)) | |
224 | continue; | |
225 | if (!is_ascii(dirent->FileName)) | |
226 | continue; | |
227 | if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */ | |
228 | continue; | |
229 | if (!endswith_no_case(dirent->FileName, u".addon.efi")) | |
230 | continue; | |
231 | ||
232 | d = xstrdup16(dirent->FileName); | |
233 | ||
234 | if (*n_items + 2 > *n_allocated) { | |
235 | /* We allocate 16 entries at a time, as a matter of optimization */ | |
236 | if (*n_items > (SIZE_MAX / sizeof(uint16_t)) - 16) /* Overflow check, just in case */ | |
237 | return log_oom(); | |
238 | ||
239 | size_t m = *n_items + 16; | |
240 | *items = xrealloc(*items, *n_allocated * sizeof(uint16_t *), m * sizeof(uint16_t *)); | |
241 | *n_allocated = m; | |
242 | } | |
243 | ||
244 | (*items)[(*n_items)++] = TAKE_PTR(d); | |
245 | (*items)[*n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */ | |
246 | } | |
247 | ||
248 | return EFI_SUCCESS; | |
249 | ||
250 | } | |
251 | ||
252 | static EFI_STATUS cmdline_append_and_measure_addons( | |
253 | EFI_HANDLE stub_image, | |
254 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image, | |
255 | const char16_t *prefix, | |
256 | const char *uname, | |
257 | bool *ret_parameters_measured, | |
258 | char16_t **cmdline_append) { | |
259 | ||
260 | _cleanup_(strv_freep) char16_t **items = NULL; | |
261 | _cleanup_(file_closep) EFI_FILE *root = NULL; | |
262 | _cleanup_free_ char16_t *buffer = NULL; | |
263 | size_t n_items = 0, n_allocated = 0; | |
264 | EFI_STATUS err; | |
265 | ||
266 | assert(stub_image); | |
267 | assert(loaded_image); | |
268 | assert(prefix); | |
269 | assert(ret_parameters_measured); | |
270 | assert(cmdline_append); | |
271 | ||
272 | if (!loaded_image->DeviceHandle) | |
273 | return EFI_SUCCESS; | |
274 | ||
275 | err = open_volume(loaded_image->DeviceHandle, &root); | |
276 | if (err == EFI_UNSUPPORTED) | |
277 | /* Error will be unsupported if the bootloader doesn't implement the file system protocol on | |
278 | * its file handles. */ | |
279 | return EFI_SUCCESS; | |
280 | if (err != EFI_SUCCESS) | |
281 | return log_error_status(err, "Unable to open root directory: %m"); | |
282 | ||
283 | err = load_addons_from_dir(root, prefix, &items, &n_items, &n_allocated); | |
284 | if (err != EFI_SUCCESS) | |
285 | return err; | |
286 | ||
287 | if (n_items == 0) | |
288 | return EFI_SUCCESS; /* Empty directory */ | |
289 | ||
290 | /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements | |
291 | * are not dependent on read order) */ | |
292 | sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16); | |
293 | ||
294 | for (size_t i = 0; i < n_items; i++) { | |
295 | size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; | |
296 | _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL; | |
297 | _cleanup_(unload_imagep) EFI_HANDLE addon = NULL; | |
298 | EFI_LOADED_IMAGE_PROTOCOL *loaded_addon = NULL; | |
299 | _cleanup_free_ char16_t *addon_spath = NULL; | |
300 | ||
301 | addon_spath = xasprintf("%ls\\%ls", prefix, items[i]); | |
302 | err = make_file_device_path(loaded_image->DeviceHandle, addon_spath, &addon_path); | |
303 | if (err != EFI_SUCCESS) | |
304 | return log_error_status(err, "Error making device path for %ls: %m", addon_spath); | |
305 | ||
306 | /* By using shim_load_image, we cover both the case where the PE files are signed with MoK | |
307 | * and with DB, and running with or without shim. */ | |
308 | err = shim_load_image(stub_image, addon_path, &addon); | |
309 | if (err != EFI_SUCCESS) { | |
310 | log_error_status(err, | |
311 | "Failed to read '%ls' from '%ls', ignoring: %m", | |
312 | items[i], | |
313 | addon_spath); | |
314 | continue; | |
315 | } | |
316 | ||
317 | err = BS->HandleProtocol(addon, | |
318 | MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), | |
319 | (void **) &loaded_addon); | |
320 | if (err != EFI_SUCCESS) | |
321 | return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]); | |
322 | ||
323 | err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs); | |
324 | if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_CMDLINE] == 0) { | |
325 | if (err == EFI_SUCCESS) | |
326 | err = EFI_NOT_FOUND; | |
327 | log_error_status(err, | |
328 | "Unable to locate embedded .cmdline section in %ls, ignoring: %m", | |
329 | items[i]); | |
330 | continue; | |
331 | } | |
332 | ||
333 | /* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */ | |
334 | if (szs[UNIFIED_SECTION_LINUX] > 0) { | |
335 | log_error_status(EFI_INVALID_PARAMETER, "%ls is a UKI, not an addon, ignoring: %m", items[i]); | |
336 | continue; | |
337 | } | |
338 | ||
339 | /* Also enforce that, in case it is specified, .uname matches as a quick way to allow | |
340 | * enforcing compatibility with a specific UKI only */ | |
341 | if (uname && szs[UNIFIED_SECTION_UNAME] > 0 && | |
342 | !strneq8(uname, | |
343 | (char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_UNAME], | |
344 | szs[UNIFIED_SECTION_UNAME])) { | |
345 | log_error(".uname mismatch between %ls and UKI, ignoring", items[i]); | |
346 | continue; | |
347 | } | |
348 | ||
349 | _cleanup_free_ char16_t *tmp = TAKE_PTR(buffer), | |
350 | *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE], | |
351 | szs[UNIFIED_SECTION_CMDLINE]); | |
352 | buffer = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); | |
353 | } | |
354 | ||
355 | mangle_stub_cmdline(buffer); | |
356 | ||
357 | if (!isempty(buffer)) { | |
358 | _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append); | |
359 | bool m = false; | |
360 | ||
361 | (void) tpm_log_load_options(buffer, &m); | |
362 | *ret_parameters_measured = m; | |
363 | ||
364 | *cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", buffer); | |
365 | } | |
366 | ||
367 | return EFI_SUCCESS; | |
368 | } | |
369 | ||
31a131bb | 370 | static EFI_STATUS run(EFI_HANDLE image) { |
23002b45 | 371 | _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; |
927ebebe JJ |
372 | size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; |
373 | size_t linux_size, initrd_size, dt_size; | |
33bc9b75 MR |
374 | EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base; |
375 | _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; | |
b30a43df | 376 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image; |
927ebebe JJ |
377 | size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; |
378 | _cleanup_free_ char16_t *cmdline = NULL; | |
72c97c19 | 379 | int sections_measured = -1, parameters_measured = -1; |
05c9f9c2 | 380 | _cleanup_free_ char *uname = NULL; |
de7ad6d4 | 381 | bool sysext_measured = false, m; |
0a1d8ac7 | 382 | uint64_t loader_features = 0; |
0fa2cac4 KS |
383 | EFI_STATUS err; |
384 | ||
b4eb2de7 | 385 | err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image); |
2a5e4fe4 | 386 | if (err != EFI_SUCCESS) |
c2c62035 | 387 | return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m"); |
0fa2cac4 | 388 | |
19f08504 | 389 | if (efivar_get_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", &loader_features) != EFI_SUCCESS || |
0a1d8ac7 JD |
390 | !FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED)) { |
391 | _cleanup_(file_closep) EFI_FILE *esp_dir = NULL; | |
392 | ||
19f08504 | 393 | err = partition_open(MAKE_GUID_PTR(ESP), loaded_image->DeviceHandle, NULL, &esp_dir); |
0a1d8ac7 JD |
394 | if (err == EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */ |
395 | (void) process_random_seed(esp_dir); | |
396 | } | |
397 | ||
6017eee9 LP |
398 | err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs); |
399 | if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) { | |
2a5e4fe4 | 400 | if (err == EFI_SUCCESS) |
65ff3d26 | 401 | err = EFI_NOT_FOUND; |
c2c62035 | 402 | return log_error_status(err, "Unable to locate embedded .linux section: %m"); |
65ff3d26 | 403 | } |
0fa2cac4 | 404 | |
72c97c19 LP |
405 | /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written |
406 | * into so far), so that we have one PCR that we can nicely write policies against because it | |
407 | * contains all static data of this image, and thus can be easily be pre-calculated. */ | |
6017eee9 | 408 | for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) { |
df7ee6f8 LP |
409 | |
410 | if (!unified_section_measure(section)) /* shall not measure? */ | |
411 | continue; | |
72c97c19 LP |
412 | |
413 | if (szs[section] == 0) /* not found */ | |
414 | continue; | |
415 | ||
df7ee6f8 LP |
416 | m = false; |
417 | ||
72c97c19 LP |
418 | /* First measure the name of the section */ |
419 | (void) tpm_log_event_ascii( | |
420 | TPM_PCR_INDEX_KERNEL_IMAGE, | |
6017eee9 LP |
421 | POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]), |
422 | strsize8(unified_sections[section]), /* including NUL byte */ | |
423 | unified_sections[section], | |
72c97c19 LP |
424 | &m); |
425 | ||
426 | sections_measured = sections_measured < 0 ? m : (sections_measured && m); | |
427 | ||
428 | /* Then measure the data of the section */ | |
429 | (void) tpm_log_event_ascii( | |
430 | TPM_PCR_INDEX_KERNEL_IMAGE, | |
431 | POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section], | |
432 | szs[section], | |
6017eee9 | 433 | unified_sections[section], |
72c97c19 LP |
434 | &m); |
435 | ||
436 | sections_measured = sections_measured < 0 ? m : (sections_measured && m); | |
437 | } | |
438 | ||
439 | /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode | |
440 | * in it which PCR was used. */ | |
441 | if (sections_measured > 0) | |
19f08504 | 442 | (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0); |
72c97c19 | 443 | |
94b81afb | 444 | /* Show splash screen as early as possible */ |
957dfcc9 | 445 | graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]); |
94b81afb | 446 | |
05c9f9c2 LB |
447 | if (szs[UNIFIED_SECTION_UNAME] > 0) |
448 | uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME], | |
449 | szs[UNIFIED_SECTION_UNAME]); | |
450 | ||
b17f3b3d | 451 | if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) { |
e6e24af5 LP |
452 | /* Let's measure the passed kernel command line into the TPM. Note that this possibly |
453 | * duplicates what we already did in the boot menu, if that was already used. However, since | |
454 | * we want the boot menu to support an EFI binary, and want to this stub to be usable from | |
455 | * any boot menu, let's measure things anyway. */ | |
599fe002 | 456 | m = false; |
927ebebe | 457 | (void) tpm_log_load_options(cmdline, &m); |
599fe002 | 458 | parameters_measured = m; |
927ebebe JJ |
459 | } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) { |
460 | cmdline = xstrn8_to_16( | |
461 | (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE], | |
462 | szs[UNIFIED_SECTION_CMDLINE]); | |
463 | mangle_stub_cmdline(cmdline); | |
0fa2cac4 KS |
464 | } |
465 | ||
05c9f9c2 LB |
466 | /* If we have any extra command line to add via PE addons, load them now and append, and |
467 | * measure the additions separately, after the embedded options, but before the smbios ones, | |
468 | * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are | |
469 | * loaded first, and the image-specific ones later, for the same reason. */ | |
470 | err = cmdline_append_and_measure_addons( | |
471 | image, | |
472 | loaded_image, | |
473 | u"\\loader\\addons", | |
474 | uname, | |
475 | &m, | |
476 | &cmdline); | |
477 | if (err != EFI_SUCCESS) | |
478 | log_error_status(err, "Error loading global addons, ignoring: %m"); | |
479 | parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); | |
480 | ||
481 | _cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath); | |
482 | err = cmdline_append_and_measure_addons( | |
483 | image, | |
484 | loaded_image, | |
485 | dropin_dir, | |
486 | uname, | |
487 | &m, | |
488 | &cmdline); | |
489 | if (err != EFI_SUCCESS) | |
490 | log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); | |
491 | parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); | |
492 | ||
4b1153cf DB |
493 | /* SMBIOS OEM Strings data is controlled by the host admin and not covered |
494 | * by the VM attestation, so MUST NOT be trusted when in a confidential VM */ | |
495 | if (!is_confidential_vm()) { | |
496 | const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); | |
497 | if (extra) { | |
498 | _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra); | |
499 | cmdline = xasprintf("%ls %ls", tmp, extra16); | |
500 | ||
501 | /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific | |
502 | * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable | |
503 | * measurements that are not under control of the machine owner. */ | |
504 | m = false; | |
505 | (void) tpm_log_load_options(extra16, &m); | |
506 | parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); | |
507 | } | |
717af0de DDM |
508 | } |
509 | ||
5a186322 | 510 | export_variables(loaded_image); |
1aa15def | 511 | |
599fe002 LP |
512 | if (pack_cpio(loaded_image, |
513 | NULL, | |
a083aed0 | 514 | u".cred", |
599fe002 LP |
515 | ".extra/credentials", |
516 | /* dir_mode= */ 0500, | |
517 | /* access_mode= */ 0400, | |
d84bdadb | 518 | /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, |
a083aed0 | 519 | u"Credentials initrd", |
599fe002 LP |
520 | &credential_initrd, |
521 | &credential_initrd_size, | |
522 | &m) == EFI_SUCCESS) | |
523 | parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); | |
524 | ||
525 | if (pack_cpio(loaded_image, | |
a083aed0 JJ |
526 | u"\\loader\\credentials", |
527 | u".cred", | |
599fe002 LP |
528 | ".extra/global_credentials", |
529 | /* dir_mode= */ 0500, | |
530 | /* access_mode= */ 0400, | |
d84bdadb | 531 | /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, |
a083aed0 | 532 | u"Global credentials initrd", |
599fe002 LP |
533 | &global_credential_initrd, |
534 | &global_credential_initrd_size, | |
535 | &m) == EFI_SUCCESS) | |
536 | parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); | |
f3b6f333 | 537 | |
de7ad6d4 LP |
538 | if (pack_cpio(loaded_image, |
539 | NULL, | |
a083aed0 | 540 | u".raw", |
de7ad6d4 LP |
541 | ".extra/sysext", |
542 | /* dir_mode= */ 0555, | |
543 | /* access_mode= */ 0444, | |
d84bdadb | 544 | /* tpm_pcr= */ TPM_PCR_INDEX_INITRD_SYSEXTS, |
a083aed0 | 545 | u"System extension initrd", |
de7ad6d4 LP |
546 | &sysext_initrd, |
547 | &sysext_initrd_size, | |
548 | &m) == EFI_SUCCESS) | |
549 | sysext_measured = m; | |
599fe002 LP |
550 | |
551 | if (parameters_measured > 0) | |
19f08504 | 552 | (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS, 0); |
de7ad6d4 | 553 | if (sysext_measured) |
19f08504 | 554 | (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0); |
845707aa | 555 | |
23002b45 LP |
556 | /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it |
557 | * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section | |
558 | * is not measured, neither as raw section (see above), nor as cpio (here), because it is the | |
5dcb9c3c | 559 | * signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't |
23002b45 LP |
560 | * itself be input for PCR measurements. */ |
561 | if (szs[UNIFIED_SECTION_PCRSIG] > 0) | |
562 | (void) pack_cpio_literal( | |
563 | (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRSIG], | |
564 | szs[UNIFIED_SECTION_PCRSIG], | |
565 | ".extra", | |
a083aed0 | 566 | u"tpm2-pcr-signature.json", |
23002b45 LP |
567 | /* dir_mode= */ 0555, |
568 | /* access_mode= */ 0444, | |
d84bdadb | 569 | /* tpm_pcr= */ UINT32_MAX, |
23002b45 LP |
570 | /* tpm_description= */ NULL, |
571 | &pcrsig_initrd, | |
572 | &pcrsig_initrd_size, | |
573 | /* ret_measured= */ NULL); | |
574 | ||
575 | /* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in | |
576 | * a cpio and also pass it to the kernel, so that it can be read from | |
577 | * /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the | |
578 | * cpio. */ | |
579 | if (szs[UNIFIED_SECTION_PCRPKEY] > 0) | |
580 | (void) pack_cpio_literal( | |
581 | (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRPKEY], | |
582 | szs[UNIFIED_SECTION_PCRPKEY], | |
583 | ".extra", | |
a083aed0 | 584 | u"tpm2-pcr-public-key.pem", |
23002b45 LP |
585 | /* dir_mode= */ 0555, |
586 | /* access_mode= */ 0444, | |
d84bdadb | 587 | /* tpm_pcr= */ UINT32_MAX, |
23002b45 LP |
588 | /* tpm_description= */ NULL, |
589 | &pcrpkey_initrd, | |
590 | &pcrpkey_initrd_size, | |
591 | /* ret_measured= */ NULL); | |
592 | ||
6017eee9 LP |
593 | linux_size = szs[UNIFIED_SECTION_LINUX]; |
594 | linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX]; | |
5b5d365d | 595 | |
6017eee9 LP |
596 | initrd_size = szs[UNIFIED_SECTION_INITRD]; |
597 | initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0; | |
37fa3690 | 598 | |
6017eee9 LP |
599 | dt_size = szs[UNIFIED_SECTION_DTB]; |
600 | dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0; | |
33bc9b75 | 601 | |
09173c91 | 602 | _cleanup_pages_ Pages initrd_pages = {}; |
64650de7 | 603 | if (credential_initrd || global_credential_initrd || sysext_initrd || pcrsig_initrd || pcrpkey_initrd) { |
845707aa LP |
604 | /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */ |
605 | err = combine_initrd( | |
606 | initrd_base, initrd_size, | |
23002b45 LP |
607 | (const void*const[]) { |
608 | credential_initrd, | |
609 | global_credential_initrd, | |
610 | sysext_initrd, | |
611 | pcrsig_initrd, | |
612 | pcrpkey_initrd, | |
613 | }, | |
614 | (const size_t[]) { | |
615 | credential_initrd_size, | |
616 | global_credential_initrd_size, | |
617 | sysext_initrd_size, | |
618 | pcrsig_initrd_size, | |
619 | pcrpkey_initrd_size, | |
620 | }, | |
621 | 5, | |
09173c91 | 622 | &initrd_pages, &initrd_size); |
2a5e4fe4 | 623 | if (err != EFI_SUCCESS) |
845707aa LP |
624 | return err; |
625 | ||
09173c91 JJ |
626 | initrd_base = initrd_pages.addr; |
627 | ||
845707aa | 628 | /* Given these might be large let's free them explicitly, quickly. */ |
f3b6f333 AV |
629 | credential_initrd = mfree(credential_initrd); |
630 | global_credential_initrd = mfree(global_credential_initrd); | |
631 | sysext_initrd = mfree(sysext_initrd); | |
23002b45 LP |
632 | pcrsig_initrd = mfree(pcrsig_initrd); |
633 | pcrpkey_initrd = mfree(pcrpkey_initrd); | |
845707aa | 634 | } |
0fa2cac4 | 635 | |
33bc9b75 MR |
636 | if (dt_size > 0) { |
637 | err = devicetree_install_from_memory( | |
638 | &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size); | |
2a5e4fe4 | 639 | if (err != EFI_SUCCESS) |
c2c62035 | 640 | log_error_status(err, "Error loading embedded devicetree: %m"); |
33bc9b75 MR |
641 | } |
642 | ||
927ebebe | 643 | err = linux_exec(image, cmdline, |
dc467928 | 644 | PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size, |
a6089431 | 645 | PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); |
e5a1b8f9 | 646 | graphics_mode(false); |
a529d818 | 647 | return err; |
0fa2cac4 | 648 | } |
6ac54809 | 649 | |
31a131bb | 650 | DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /*wait_for_debugger=*/false); |