Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <linux/dm-ioctl.h>
4 : : #include <linux/loop.h>
5 : : #include <sys/mount.h>
6 : : #include <sys/prctl.h>
7 : : #include <sys/wait.h>
8 : :
9 : : #include "sd-device.h"
10 : : #include "sd-id128.h"
11 : :
12 : : #include "architecture.h"
13 : : #include "ask-password-api.h"
14 : : #include "blkid-util.h"
15 : : #include "blockdev-util.h"
16 : : #include "copy.h"
17 : : #include "crypt-util.h"
18 : : #include "def.h"
19 : : #include "device-nodes.h"
20 : : #include "device-util.h"
21 : : #include "dissect-image.h"
22 : : #include "dm-util.h"
23 : : #include "env-file.h"
24 : : #include "fd-util.h"
25 : : #include "fileio.h"
26 : : #include "fs-util.h"
27 : : #include "gpt.h"
28 : : #include "hexdecoct.h"
29 : : #include "hostname-util.h"
30 : : #include "id128-util.h"
31 : : #include "missing.h"
32 : : #include "mount-util.h"
33 : : #include "mountpoint-util.h"
34 : : #include "nulstr-util.h"
35 : : #include "os-util.h"
36 : : #include "path-util.h"
37 : : #include "process-util.h"
38 : : #include "raw-clone.h"
39 : : #include "signal-util.h"
40 : : #include "stat-util.h"
41 : : #include "stdio-util.h"
42 : : #include "string-table.h"
43 : : #include "string-util.h"
44 : : #include "strv.h"
45 : : #include "tmpfile-util.h"
46 : : #include "udev-util.h"
47 : : #include "user-util.h"
48 : : #include "xattr-util.h"
49 : :
50 : 0 : int probe_filesystem(const char *node, char **ret_fstype) {
51 : : /* Try to find device content type and return it in *ret_fstype. If nothing is found,
52 : : * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
53 : : * different error otherwise. */
54 : :
55 : : #if HAVE_BLKID
56 : 0 : _cleanup_(blkid_free_probep) blkid_probe b = NULL;
57 : : const char *fstype;
58 : : int r;
59 : :
60 : 0 : errno = 0;
61 : 0 : b = blkid_new_probe_from_filename(node);
62 [ # # ]: 0 : if (!b)
63 : 0 : return errno_or_else(ENOMEM);
64 : :
65 : 0 : blkid_probe_enable_superblocks(b, 1);
66 : 0 : blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
67 : :
68 : 0 : errno = 0;
69 : 0 : r = blkid_do_safeprobe(b);
70 [ # # ]: 0 : if (r == 1) {
71 [ # # ]: 0 : log_debug("No type detected on partition %s", node);
72 : 0 : goto not_found;
73 : : }
74 [ # # ]: 0 : if (r == -2) {
75 [ # # ]: 0 : log_debug("Results ambiguous for partition %s", node);
76 : 0 : return -EUCLEAN;
77 : : }
78 [ # # ]: 0 : if (r != 0)
79 : 0 : return errno_or_else(EIO);
80 : :
81 : 0 : (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
82 : :
83 [ # # ]: 0 : if (fstype) {
84 : : char *t;
85 : :
86 : 0 : t = strdup(fstype);
87 [ # # ]: 0 : if (!t)
88 : 0 : return -ENOMEM;
89 : :
90 : 0 : *ret_fstype = t;
91 : 0 : return 1;
92 : : }
93 : :
94 : 0 : not_found:
95 : 0 : *ret_fstype = NULL;
96 : 0 : return 0;
97 : : #else
98 : : return -EOPNOTSUPP;
99 : : #endif
100 : : }
101 : :
102 : : #if HAVE_BLKID
103 : : /* Detect RPMB and Boot partitions, which are not listed by blkid.
104 : : * See https://github.com/systemd/systemd/issues/5806. */
105 : 0 : static bool device_is_mmc_special_partition(sd_device *d) {
106 : : const char *sysname;
107 : :
108 [ # # ]: 0 : assert(d);
109 : :
110 [ # # ]: 0 : if (sd_device_get_sysname(d, &sysname) < 0)
111 : 0 : return false;
112 : :
113 [ # # ]: 0 : return startswith(sysname, "mmcblk") &&
114 [ # # # # : 0 : (endswith(sysname, "rpmb") || endswith(sysname, "boot0") || endswith(sysname, "boot1"));
# # ]
115 : : }
116 : :
117 : 0 : static bool device_is_block(sd_device *d) {
118 : : const char *ss;
119 : :
120 [ # # ]: 0 : assert(d);
121 : :
122 [ # # ]: 0 : if (sd_device_get_subsystem(d, &ss) < 0)
123 : 0 : return false;
124 : :
125 : 0 : return streq(ss, "block");
126 : : }
127 : :
128 : 0 : static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
129 : 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
130 : : int r;
131 : :
132 [ # # ]: 0 : assert(d);
133 [ # # ]: 0 : assert(ret);
134 : :
135 : 0 : r = sd_device_enumerator_new(&e);
136 [ # # ]: 0 : if (r < 0)
137 : 0 : return r;
138 : :
139 : 0 : r = sd_device_enumerator_allow_uninitialized(e);
140 [ # # ]: 0 : if (r < 0)
141 : 0 : return r;
142 : :
143 : 0 : r = sd_device_enumerator_add_match_parent(e, d);
144 [ # # ]: 0 : if (r < 0)
145 : 0 : return r;
146 : :
147 : 0 : *ret = TAKE_PTR(e);
148 : 0 : return 0;
149 : : }
150 : :
151 : : /* how many times to wait for the device nodes to appear */
152 : : #define N_DEVICE_NODE_LIST_ATTEMPTS 10
153 : :
154 : 0 : static int wait_for_partitions_to_appear(
155 : : int fd,
156 : : sd_device *d,
157 : : unsigned num_partitions,
158 : : DissectImageFlags flags,
159 : : sd_device_enumerator **ret_enumerator) {
160 : :
161 : 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
162 : : sd_device *q;
163 : : unsigned n;
164 : : int r;
165 : :
166 [ # # ]: 0 : assert(fd >= 0);
167 [ # # ]: 0 : assert(d);
168 [ # # ]: 0 : assert(ret_enumerator);
169 : :
170 : 0 : r = enumerator_for_parent(d, &e);
171 [ # # ]: 0 : if (r < 0)
172 : 0 : return r;
173 : :
174 : : /* Count the partitions enumerated by the kernel */
175 : 0 : n = 0;
176 [ # # ]: 0 : FOREACH_DEVICE(e, q) {
177 [ # # ]: 0 : if (sd_device_get_devnum(q, NULL) < 0)
178 : 0 : continue;
179 [ # # ]: 0 : if (!device_is_block(q))
180 : 0 : continue;
181 [ # # ]: 0 : if (device_is_mmc_special_partition(q))
182 : 0 : continue;
183 : :
184 [ # # ]: 0 : if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
185 : 0 : r = device_wait_for_initialization(q, "block", USEC_INFINITY, NULL);
186 [ # # ]: 0 : if (r < 0)
187 : 0 : return r;
188 : : }
189 : :
190 : 0 : n++;
191 : : }
192 : :
193 [ # # ]: 0 : if (n == num_partitions + 1) {
194 : 0 : *ret_enumerator = TAKE_PTR(e);
195 : 0 : return 0; /* success! */
196 : : }
197 [ # # ]: 0 : if (n > num_partitions + 1)
198 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(EIO),
199 : : "blkid and kernel partition lists do not match.");
200 : :
201 : : /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running or it
202 : : * got EBUSY because udev already opened the device. Let's reprobe the device, which is a synchronous
203 : : * call that waits until probing is complete. */
204 : :
205 : 0 : for (unsigned j = 0; ; j++) {
206 [ # # ]: 0 : if (j++ > 20)
207 : 0 : return -EBUSY;
208 : :
209 [ # # ]: 0 : if (ioctl(fd, BLKRRPART, 0) >= 0)
210 : 0 : break;
211 : 0 : r = -errno;
212 [ # # ]: 0 : if (r == -EINVAL) {
213 : : struct loop_info64 info;
214 : :
215 : : /* If we are running on a loop device that has partition scanning off, return
216 : : * an explicit recognizable error about this, so that callers can generate a
217 : : * proper message explaining the situation. */
218 : :
219 [ # # # # ]: 0 : if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
220 [ # # ]: 0 : log_debug("Device is a loop device and partition scanning is off!");
221 : 0 : return -EPROTONOSUPPORT;
222 : : }
223 : : }
224 [ # # ]: 0 : if (r != -EBUSY)
225 : 0 : return r;
226 : :
227 : : /* If something else has the device open, such as an udev rule, the ioctl will return
228 : : * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a bit,
229 : : * and try again.
230 : : *
231 : : * This is really something they should fix in the kernel! */
232 : 0 : (void) usleep(50 * USEC_PER_MSEC);
233 : :
234 : : }
235 : :
236 : 0 : return -EAGAIN; /* no success yet, try again */
237 : : }
238 : :
239 : 0 : static int loop_wait_for_partitions_to_appear(
240 : : int fd,
241 : : sd_device *d,
242 : : unsigned num_partitions,
243 : : DissectImageFlags flags,
244 : : sd_device_enumerator **ret_enumerator) {
245 : 0 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
246 : : int r;
247 : :
248 [ # # ]: 0 : assert(fd >= 0);
249 [ # # ]: 0 : assert(d);
250 [ # # ]: 0 : assert(ret_enumerator);
251 : :
252 [ # # ]: 0 : log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions);
253 : :
254 [ # # ]: 0 : if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
255 : 0 : r = device_wait_for_initialization(d, "block", USEC_INFINITY, &device);
256 [ # # ]: 0 : if (r < 0)
257 : 0 : return r;
258 : : } else
259 : 0 : device = sd_device_ref(d);
260 : :
261 [ # # ]: 0 : for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
262 : 0 : r = wait_for_partitions_to_appear(fd, device, num_partitions, flags, ret_enumerator);
263 [ # # ]: 0 : if (r != -EAGAIN)
264 : 0 : return r;
265 : : }
266 : :
267 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
268 : : "Kernel partitions dit not appear within %d attempts",
269 : : N_DEVICE_NODE_LIST_ATTEMPTS);
270 : : }
271 : :
272 : : #endif
273 : :
274 : 0 : int dissect_image(
275 : : int fd,
276 : : const void *root_hash,
277 : : size_t root_hash_size,
278 : : DissectImageFlags flags,
279 : : DissectedImage **ret) {
280 : :
281 : : #if HAVE_BLKID
282 : 0 : sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
283 : 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
284 : 0 : bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
285 : 0 : _cleanup_(sd_device_unrefp) sd_device *d = NULL;
286 : 0 : _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
287 : 0 : _cleanup_(blkid_free_probep) blkid_probe b = NULL;
288 : 0 : _cleanup_free_ char *generic_node = NULL;
289 : 0 : sd_id128_t generic_uuid = SD_ID128_NULL;
290 : 0 : const char *pttype = NULL;
291 : : blkid_partlist pl;
292 : : int r, generic_nr;
293 : : struct stat st;
294 : : sd_device *q;
295 : : unsigned i;
296 : :
297 [ # # ]: 0 : assert(fd >= 0);
298 [ # # ]: 0 : assert(ret);
299 [ # # # # ]: 0 : assert(root_hash || root_hash_size == 0);
300 : :
301 : : /* Probes a disk image, and returns information about what it found in *ret.
302 : : *
303 : : * Returns -ENOPKG if no suitable partition table or file system could be found.
304 : : * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
305 : :
306 [ # # ]: 0 : if (root_hash) {
307 : : /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
308 : : * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
309 : : * 128bit. */
310 : :
311 [ # # ]: 0 : if (root_hash_size < sizeof(sd_id128_t))
312 : 0 : return -EINVAL;
313 : :
314 : 0 : memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
315 : 0 : memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
316 : :
317 [ # # ]: 0 : if (sd_id128_is_null(root_uuid))
318 : 0 : return -EINVAL;
319 [ # # ]: 0 : if (sd_id128_is_null(verity_uuid))
320 : 0 : return -EINVAL;
321 : : }
322 : :
323 [ # # ]: 0 : if (fstat(fd, &st) < 0)
324 : 0 : return -errno;
325 : :
326 [ # # ]: 0 : if (!S_ISBLK(st.st_mode))
327 : 0 : return -ENOTBLK;
328 : :
329 : 0 : b = blkid_new_probe();
330 [ # # ]: 0 : if (!b)
331 : 0 : return -ENOMEM;
332 : :
333 : 0 : errno = 0;
334 : 0 : r = blkid_probe_set_device(b, fd, 0, 0);
335 [ # # ]: 0 : if (r != 0)
336 : 0 : return errno_or_else(ENOMEM);
337 : :
338 [ # # ]: 0 : if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
339 : : /* Look for file system superblocks, unless we only shall look for GPT partition tables */
340 : 0 : blkid_probe_enable_superblocks(b, 1);
341 : 0 : blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
342 : : }
343 : :
344 : 0 : blkid_probe_enable_partitions(b, 1);
345 : 0 : blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
346 : :
347 : 0 : errno = 0;
348 : 0 : r = blkid_do_safeprobe(b);
349 [ # # # # ]: 0 : if (IN_SET(r, -2, 1))
350 [ # # ]: 0 : return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
351 [ # # ]: 0 : if (r != 0)
352 : 0 : return errno_or_else(EIO);
353 : :
354 : 0 : m = new0(DissectedImage, 1);
355 [ # # ]: 0 : if (!m)
356 : 0 : return -ENOMEM;
357 : :
358 : 0 : r = sd_device_new_from_devnum(&d, 'b', st.st_rdev);
359 [ # # ]: 0 : if (r < 0)
360 : 0 : return r;
361 : :
362 [ # # ]: 0 : if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
363 [ # # ]: 0 : (flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
364 : 0 : const char *usage = NULL;
365 : :
366 : 0 : (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
367 [ # # # # : 0 : if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
# # ]
368 : 0 : _cleanup_free_ char *t = NULL, *n = NULL;
369 : 0 : const char *fstype = NULL;
370 : :
371 : : /* OK, we have found a file system, that's our root partition then. */
372 : 0 : (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
373 : :
374 [ # # ]: 0 : if (fstype) {
375 : 0 : t = strdup(fstype);
376 [ # # ]: 0 : if (!t)
377 : 0 : return -ENOMEM;
378 : : }
379 : :
380 : 0 : r = device_path_make_major_minor(st.st_mode, st.st_rdev, &n);
381 [ # # ]: 0 : if (r < 0)
382 : 0 : return r;
383 : :
384 : 0 : m->partitions[PARTITION_ROOT] = (DissectedPartition) {
385 : : .found = true,
386 : : .rw = true,
387 : : .partno = -1,
388 : : .architecture = _ARCHITECTURE_INVALID,
389 : 0 : .fstype = TAKE_PTR(t),
390 : 0 : .node = TAKE_PTR(n),
391 : : };
392 : :
393 : 0 : m->encrypted = streq_ptr(fstype, "crypto_LUKS");
394 : :
395 : 0 : r = loop_wait_for_partitions_to_appear(fd, d, 0, flags, &e);
396 [ # # ]: 0 : if (r < 0)
397 : 0 : return r;
398 : :
399 : 0 : *ret = TAKE_PTR(m);
400 : :
401 : 0 : return 0;
402 : : }
403 : : }
404 : :
405 : 0 : (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
406 [ # # ]: 0 : if (!pttype)
407 : 0 : return -ENOPKG;
408 : :
409 : 0 : is_gpt = streq_ptr(pttype, "gpt");
410 : 0 : is_mbr = streq_ptr(pttype, "dos");
411 : :
412 [ # # # # : 0 : if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
# # ]
413 : 0 : return -ENOPKG;
414 : :
415 : 0 : errno = 0;
416 : 0 : pl = blkid_probe_get_partitions(b);
417 [ # # ]: 0 : if (!pl)
418 : 0 : return errno_or_else(ENOMEM);
419 : :
420 : 0 : r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), flags, &e);
421 [ # # ]: 0 : if (r < 0)
422 : 0 : return r;
423 : :
424 [ # # ]: 0 : FOREACH_DEVICE(e, q) {
425 : : unsigned long long pflags;
426 : : blkid_partition pp;
427 : : const char *node;
428 : : dev_t qn;
429 : : int nr;
430 : :
431 : 0 : r = sd_device_get_devnum(q, &qn);
432 [ # # ]: 0 : if (r < 0)
433 : 0 : continue;
434 : :
435 [ # # ]: 0 : if (st.st_rdev == qn)
436 : 0 : continue;
437 : :
438 [ # # ]: 0 : if (!device_is_block(q))
439 : 0 : continue;
440 : :
441 [ # # ]: 0 : if (device_is_mmc_special_partition(q))
442 : 0 : continue;
443 : :
444 : 0 : r = sd_device_get_devname(q, &node);
445 [ # # ]: 0 : if (r < 0)
446 : 0 : continue;
447 : :
448 : 0 : pp = blkid_partlist_devno_to_partition(pl, qn);
449 [ # # ]: 0 : if (!pp)
450 : 0 : continue;
451 : :
452 : 0 : pflags = blkid_partition_get_flags(pp);
453 : :
454 : 0 : nr = blkid_partition_get_partno(pp);
455 [ # # ]: 0 : if (nr < 0)
456 : 0 : continue;
457 : :
458 [ # # ]: 0 : if (is_gpt) {
459 : 0 : int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
460 : 0 : const char *stype, *sid, *fstype = NULL;
461 : : sd_id128_t type_id, id;
462 : 0 : bool rw = true;
463 : :
464 : 0 : sid = blkid_partition_get_uuid(pp);
465 [ # # ]: 0 : if (!sid)
466 : 0 : continue;
467 [ # # ]: 0 : if (sd_id128_from_string(sid, &id) < 0)
468 : 0 : continue;
469 : :
470 : 0 : stype = blkid_partition_get_type_string(pp);
471 [ # # ]: 0 : if (!stype)
472 : 0 : continue;
473 [ # # ]: 0 : if (sd_id128_from_string(stype, &type_id) < 0)
474 : 0 : continue;
475 : :
476 [ # # ]: 0 : if (sd_id128_equal(type_id, GPT_HOME)) {
477 : :
478 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
479 : 0 : continue;
480 : :
481 : 0 : designator = PARTITION_HOME;
482 : 0 : rw = !(pflags & GPT_FLAG_READ_ONLY);
483 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_SRV)) {
484 : :
485 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
486 : 0 : continue;
487 : :
488 : 0 : designator = PARTITION_SRV;
489 : 0 : rw = !(pflags & GPT_FLAG_READ_ONLY);
490 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_ESP)) {
491 : :
492 : : /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined
493 : : * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the
494 : : * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */
495 : :
496 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
497 : 0 : continue;
498 : :
499 : 0 : designator = PARTITION_ESP;
500 : 0 : fstype = "vfat";
501 : :
502 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
503 : :
504 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
505 : 0 : continue;
506 : :
507 : 0 : designator = PARTITION_XBOOTLDR;
508 : 0 : rw = !(pflags & GPT_FLAG_READ_ONLY);
509 : : }
510 : : #ifdef GPT_ROOT_NATIVE
511 [ # # ]: 0 : else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
512 : :
513 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
514 : 0 : continue;
515 : :
516 : : /* If a root ID is specified, ignore everything but the root id */
517 [ # # # # ]: 0 : if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
518 : 0 : continue;
519 : :
520 : 0 : designator = PARTITION_ROOT;
521 : 0 : architecture = native_architecture();
522 : 0 : rw = !(pflags & GPT_FLAG_READ_ONLY);
523 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
524 : :
525 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
526 : 0 : continue;
527 : :
528 : 0 : m->can_verity = true;
529 : :
530 : : /* Ignore verity unless a root hash is specified */
531 [ # # # # ]: 0 : if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
532 : 0 : continue;
533 : :
534 : 0 : designator = PARTITION_ROOT_VERITY;
535 : 0 : fstype = "DM_verity_hash";
536 : 0 : architecture = native_architecture();
537 : 0 : rw = false;
538 : : }
539 : : #endif
540 : : #ifdef GPT_ROOT_SECONDARY
541 [ # # ]: 0 : else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
542 : :
543 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
544 : 0 : continue;
545 : :
546 : : /* If a root ID is specified, ignore everything but the root id */
547 [ # # # # ]: 0 : if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
548 : 0 : continue;
549 : :
550 : 0 : designator = PARTITION_ROOT_SECONDARY;
551 : 0 : architecture = SECONDARY_ARCHITECTURE;
552 : 0 : rw = !(pflags & GPT_FLAG_READ_ONLY);
553 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
554 : :
555 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
556 : 0 : continue;
557 : :
558 : 0 : m->can_verity = true;
559 : :
560 : : /* Ignore verity unless root has is specified */
561 [ # # # # ]: 0 : if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
562 : 0 : continue;
563 : :
564 : 0 : designator = PARTITION_ROOT_SECONDARY_VERITY;
565 : 0 : fstype = "DM_verity_hash";
566 : 0 : architecture = SECONDARY_ARCHITECTURE;
567 : 0 : rw = false;
568 : : }
569 : : #endif
570 [ # # ]: 0 : else if (sd_id128_equal(type_id, GPT_SWAP)) {
571 : :
572 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
573 : 0 : continue;
574 : :
575 : 0 : designator = PARTITION_SWAP;
576 : 0 : fstype = "swap";
577 [ # # ]: 0 : } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
578 : :
579 [ # # ]: 0 : if (pflags & GPT_FLAG_NO_AUTO)
580 : 0 : continue;
581 : :
582 [ # # ]: 0 : if (generic_node)
583 : 0 : multiple_generic = true;
584 : : else {
585 : 0 : generic_nr = nr;
586 : 0 : generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
587 : 0 : generic_uuid = id;
588 : 0 : generic_node = strdup(node);
589 [ # # ]: 0 : if (!generic_node)
590 : 0 : return -ENOMEM;
591 : : }
592 : : }
593 : :
594 [ # # ]: 0 : if (designator != _PARTITION_DESIGNATOR_INVALID) {
595 [ # # # # : 0 : _cleanup_free_ char *t = NULL, *n = NULL;
# # ]
596 : :
597 : : /* First one wins */
598 [ # # ]: 0 : if (m->partitions[designator].found)
599 : 0 : continue;
600 : :
601 [ # # ]: 0 : if (fstype) {
602 : 0 : t = strdup(fstype);
603 [ # # ]: 0 : if (!t)
604 : 0 : return -ENOMEM;
605 : : }
606 : :
607 : 0 : n = strdup(node);
608 [ # # ]: 0 : if (!n)
609 : 0 : return -ENOMEM;
610 : :
611 : 0 : m->partitions[designator] = (DissectedPartition) {
612 : : .found = true,
613 : : .partno = nr,
614 : : .rw = rw,
615 : : .architecture = architecture,
616 : 0 : .node = TAKE_PTR(n),
617 : 0 : .fstype = TAKE_PTR(t),
618 : : .uuid = id,
619 : : };
620 : : }
621 : :
622 [ # # ]: 0 : } else if (is_mbr) {
623 : :
624 [ # # # ]: 0 : switch (blkid_partition_get_type(pp)) {
625 : :
626 : 0 : case 0x83: /* Linux partition */
627 : :
628 [ # # ]: 0 : if (pflags != 0x80) /* Bootable flag */
629 : 0 : continue;
630 : :
631 [ # # ]: 0 : if (generic_node)
632 : 0 : multiple_generic = true;
633 : : else {
634 : 0 : generic_nr = nr;
635 : 0 : generic_rw = true;
636 : 0 : generic_node = strdup(node);
637 [ # # ]: 0 : if (!generic_node)
638 : 0 : return -ENOMEM;
639 : : }
640 : :
641 : 0 : break;
642 : :
643 : 0 : case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
644 [ # # # ]: 0 : _cleanup_free_ char *n = NULL;
645 : 0 : sd_id128_t id = SD_ID128_NULL;
646 : : const char *sid;
647 : :
648 : : /* First one wins */
649 [ # # ]: 0 : if (m->partitions[PARTITION_XBOOTLDR].found)
650 : 0 : continue;
651 : :
652 : 0 : sid = blkid_partition_get_uuid(pp);
653 [ # # ]: 0 : if (sid)
654 : 0 : (void) sd_id128_from_string(sid, &id);
655 : :
656 : 0 : n = strdup(node);
657 [ # # ]: 0 : if (!n)
658 : 0 : return -ENOMEM;
659 : :
660 : 0 : m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
661 : : .found = true,
662 : : .partno = nr,
663 : : .rw = true,
664 : : .architecture = _ARCHITECTURE_INVALID,
665 : 0 : .node = TAKE_PTR(n),
666 : : .uuid = id,
667 : : };
668 : :
669 : 0 : break;
670 : : }}
671 : 0 : }
672 : : }
673 : :
674 [ # # ]: 0 : if (!m->partitions[PARTITION_ROOT].found) {
675 : : /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
676 : : * either, then check if there's a single generic one, and use that. */
677 : :
678 [ # # ]: 0 : if (m->partitions[PARTITION_ROOT_VERITY].found)
679 : 0 : return -EADDRNOTAVAIL;
680 : :
681 [ # # ]: 0 : if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
682 : 0 : m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
683 [ # # ]: 0 : zero(m->partitions[PARTITION_ROOT_SECONDARY]);
684 : :
685 : 0 : m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
686 [ # # ]: 0 : zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
687 : :
688 [ # # ]: 0 : } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
689 : :
690 : : /* If the root has was set, then we won't fallback to a generic node, because the root hash
691 : : * decides */
692 [ # # ]: 0 : if (root_hash)
693 : 0 : return -EADDRNOTAVAIL;
694 : :
695 : : /* If we didn't find a generic node, then we can't fix this up either */
696 [ # # ]: 0 : if (!generic_node)
697 : 0 : return -ENXIO;
698 : :
699 : : /* If we didn't find a properly marked root partition, but we did find a single suitable
700 : : * generic Linux partition, then use this as root partition, if the caller asked for it. */
701 [ # # ]: 0 : if (multiple_generic)
702 : 0 : return -ENOTUNIQ;
703 : :
704 : 0 : m->partitions[PARTITION_ROOT] = (DissectedPartition) {
705 : : .found = true,
706 : : .rw = generic_rw,
707 : : .partno = generic_nr,
708 : : .architecture = _ARCHITECTURE_INVALID,
709 : 0 : .node = TAKE_PTR(generic_node),
710 : : .uuid = generic_uuid,
711 : : };
712 : : }
713 : : }
714 : :
715 [ # # ]: 0 : if (root_hash) {
716 [ # # # # ]: 0 : if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
717 : 0 : return -EADDRNOTAVAIL;
718 : :
719 : : /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
720 : : * (which would be weird, after all the root hash should only be assigned to one pair of
721 : : * partitions... */
722 : 0 : m->partitions[PARTITION_ROOT_SECONDARY].found = false;
723 : 0 : m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
724 : :
725 : : /* If we found a verity setup, then the root partition is necessarily read-only. */
726 : 0 : m->partitions[PARTITION_ROOT].rw = false;
727 : :
728 : 0 : m->verity = true;
729 : : }
730 : :
731 : 0 : blkid_free_probe(b);
732 : 0 : b = NULL;
733 : :
734 : : /* Fill in file system types if we don't know them yet. */
735 [ # # ]: 0 : for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
736 : 0 : DissectedPartition *p = m->partitions + i;
737 : :
738 [ # # ]: 0 : if (!p->found)
739 : 0 : continue;
740 : :
741 [ # # # # ]: 0 : if (!p->fstype && p->node) {
742 : 0 : r = probe_filesystem(p->node, &p->fstype);
743 [ # # # # ]: 0 : if (r < 0 && r != -EUCLEAN)
744 : 0 : return r;
745 : : }
746 : :
747 [ # # ]: 0 : if (streq_ptr(p->fstype, "crypto_LUKS"))
748 : 0 : m->encrypted = true;
749 : :
750 [ # # # # ]: 0 : if (p->fstype && fstype_is_ro(p->fstype))
751 : 0 : p->rw = false;
752 : : }
753 : :
754 : 0 : *ret = TAKE_PTR(m);
755 : :
756 : 0 : return 0;
757 : : #else
758 : : return -EOPNOTSUPP;
759 : : #endif
760 : : }
761 : :
762 : 0 : DissectedImage* dissected_image_unref(DissectedImage *m) {
763 : : unsigned i;
764 : :
765 [ # # ]: 0 : if (!m)
766 : 0 : return NULL;
767 : :
768 [ # # ]: 0 : for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
769 : 0 : free(m->partitions[i].fstype);
770 : 0 : free(m->partitions[i].node);
771 : 0 : free(m->partitions[i].decrypted_fstype);
772 : 0 : free(m->partitions[i].decrypted_node);
773 : : }
774 : :
775 : 0 : free(m->hostname);
776 : 0 : strv_free(m->machine_info);
777 : 0 : strv_free(m->os_release);
778 : :
779 : 0 : return mfree(m);
780 : : }
781 : :
782 : 0 : static int is_loop_device(const char *path) {
783 : 0 : char s[SYS_BLOCK_PATH_MAX("/../loop/")];
784 : : struct stat st;
785 : :
786 [ # # ]: 0 : assert(path);
787 : :
788 [ # # ]: 0 : if (stat(path, &st) < 0)
789 : 0 : return -errno;
790 : :
791 [ # # ]: 0 : if (!S_ISBLK(st.st_mode))
792 : 0 : return -ENOTBLK;
793 : :
794 [ # # ]: 0 : xsprintf_sys_block_path(s, "/loop/", st.st_dev);
795 [ # # ]: 0 : if (access(s, F_OK) < 0) {
796 [ # # ]: 0 : if (errno != ENOENT)
797 : 0 : return -errno;
798 : :
799 : : /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
800 [ # # ]: 0 : xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
801 [ # # ]: 0 : if (access(s, F_OK) < 0)
802 [ # # ]: 0 : return errno == ENOENT ? false : -errno;
803 : : }
804 : :
805 : 0 : return true;
806 : : }
807 : :
808 : 0 : static int mount_partition(
809 : : DissectedPartition *m,
810 : : const char *where,
811 : : const char *directory,
812 : : uid_t uid_shift,
813 : : DissectImageFlags flags) {
814 : :
815 : 0 : _cleanup_free_ char *chased = NULL, *options = NULL;
816 : : const char *p, *node, *fstype;
817 : : bool rw;
818 : : int r;
819 : :
820 [ # # ]: 0 : assert(m);
821 [ # # ]: 0 : assert(where);
822 : :
823 [ # # ]: 0 : node = m->decrypted_node ?: m->node;
824 [ # # ]: 0 : fstype = m->decrypted_fstype ?: m->fstype;
825 : :
826 [ # # # # : 0 : if (!m->found || !node || !fstype)
# # ]
827 : 0 : return 0;
828 : :
829 : : /* Stacked encryption? Yuck */
830 [ # # ]: 0 : if (streq_ptr(fstype, "crypto_LUKS"))
831 : 0 : return -ELOOP;
832 : :
833 [ # # # # ]: 0 : rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
834 : :
835 [ # # ]: 0 : if (directory) {
836 : 0 : r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased);
837 [ # # ]: 0 : if (r < 0)
838 : 0 : return r;
839 : :
840 : 0 : p = chased;
841 : : } else
842 : 0 : p = where;
843 : :
844 : : /* If requested, turn on discard support. */
845 [ # # ]: 0 : if (fstype_can_discard(fstype) &&
846 [ # # ]: 0 : ((flags & DISSECT_IMAGE_DISCARD) ||
847 [ # # # # ]: 0 : ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node)))) {
848 : 0 : options = strdup("discard");
849 [ # # ]: 0 : if (!options)
850 : 0 : return -ENOMEM;
851 : : }
852 : :
853 [ # # # # : 0 : if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
# # ]
854 [ # # ]: 0 : _cleanup_free_ char *uid_option = NULL;
855 : :
856 [ # # ]: 0 : if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
857 : 0 : return -ENOMEM;
858 : :
859 [ # # ]: 0 : if (!strextend_with_separator(&options, ",", uid_option, NULL))
860 : 0 : return -ENOMEM;
861 : : }
862 : :
863 [ # # ]: 0 : r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
864 [ # # ]: 0 : if (r < 0)
865 : 0 : return r;
866 : :
867 : 0 : return 1;
868 : : }
869 : :
870 : 0 : int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
871 : : int r, boot_mounted;
872 : :
873 [ # # ]: 0 : assert(m);
874 [ # # ]: 0 : assert(where);
875 : :
876 [ # # ]: 0 : if (!m->partitions[PARTITION_ROOT].found)
877 : 0 : return -ENXIO;
878 : :
879 [ # # ]: 0 : if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
880 : 0 : r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
881 [ # # ]: 0 : if (r < 0)
882 : 0 : return r;
883 : :
884 [ # # ]: 0 : if (flags & DISSECT_IMAGE_VALIDATE_OS) {
885 : 0 : r = path_is_os_tree(where);
886 [ # # ]: 0 : if (r < 0)
887 : 0 : return r;
888 [ # # ]: 0 : if (r == 0)
889 : 0 : return -EMEDIUMTYPE;
890 : : }
891 : : }
892 : :
893 [ # # ]: 0 : if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
894 : 0 : return 0;
895 : :
896 : 0 : r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
897 [ # # ]: 0 : if (r < 0)
898 : 0 : return r;
899 : :
900 : 0 : r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
901 [ # # ]: 0 : if (r < 0)
902 : 0 : return r;
903 : :
904 : 0 : boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
905 [ # # ]: 0 : if (boot_mounted < 0)
906 : 0 : return boot_mounted;
907 : :
908 [ # # ]: 0 : if (m->partitions[PARTITION_ESP].found) {
909 : : /* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it
910 : : * exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */
911 : :
912 : 0 : r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL);
913 [ # # ]: 0 : if (r >= 0) {
914 : 0 : r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
915 [ # # ]: 0 : if (r < 0)
916 : 0 : return r;
917 : :
918 [ # # ]: 0 : } else if (boot_mounted <= 0) {
919 [ # # ]: 0 : _cleanup_free_ char *p = NULL;
920 : :
921 : 0 : r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p);
922 [ # # # # ]: 0 : if (r >= 0 && dir_is_empty(p) > 0) {
923 : 0 : r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
924 [ # # ]: 0 : if (r < 0)
925 : 0 : return r;
926 : : }
927 : : }
928 : : }
929 : :
930 : 0 : return 0;
931 : : }
932 : :
933 : : #if HAVE_LIBCRYPTSETUP
934 : : typedef struct DecryptedPartition {
935 : : struct crypt_device *device;
936 : : char *name;
937 : : bool relinquished;
938 : : } DecryptedPartition;
939 : :
940 : : struct DecryptedImage {
941 : : DecryptedPartition *decrypted;
942 : : size_t n_decrypted;
943 : : size_t n_allocated;
944 : : };
945 : : #endif
946 : :
947 : 0 : DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
948 : : #if HAVE_LIBCRYPTSETUP
949 : : size_t i;
950 : : int r;
951 : :
952 [ # # ]: 0 : if (!d)
953 : 0 : return NULL;
954 : :
955 [ # # ]: 0 : for (i = 0; i < d->n_decrypted; i++) {
956 : 0 : DecryptedPartition *p = d->decrypted + i;
957 : :
958 [ # # # # : 0 : if (p->device && p->name && !p->relinquished) {
# # ]
959 : 0 : r = crypt_deactivate(p->device, p->name);
960 [ # # ]: 0 : if (r < 0)
961 [ # # ]: 0 : log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
962 : : }
963 : :
964 [ # # ]: 0 : if (p->device)
965 : 0 : crypt_free(p->device);
966 : 0 : free(p->name);
967 : : }
968 : :
969 : 0 : free(d);
970 : : #endif
971 : 0 : return NULL;
972 : : }
973 : :
974 : : #if HAVE_LIBCRYPTSETUP
975 : :
976 : 0 : static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
977 : 0 : _cleanup_free_ char *name = NULL, *node = NULL;
978 : : const char *base;
979 : :
980 [ # # ]: 0 : assert(original_node);
981 [ # # ]: 0 : assert(suffix);
982 [ # # ]: 0 : assert(ret_name);
983 [ # # ]: 0 : assert(ret_node);
984 : :
985 : 0 : base = strrchr(original_node, '/');
986 [ # # ]: 0 : if (!base)
987 : 0 : return -EINVAL;
988 : 0 : base++;
989 [ # # ]: 0 : if (isempty(base))
990 : 0 : return -EINVAL;
991 : :
992 : 0 : name = strjoin(base, suffix);
993 [ # # ]: 0 : if (!name)
994 : 0 : return -ENOMEM;
995 [ # # ]: 0 : if (!filename_is_valid(name))
996 : 0 : return -EINVAL;
997 : :
998 : 0 : node = path_join(crypt_get_dir(), name);
999 [ # # ]: 0 : if (!node)
1000 : 0 : return -ENOMEM;
1001 : :
1002 : 0 : *ret_name = TAKE_PTR(name);
1003 : 0 : *ret_node = TAKE_PTR(node);
1004 : :
1005 : 0 : return 0;
1006 : : }
1007 : :
1008 : 0 : static int decrypt_partition(
1009 : : DissectedPartition *m,
1010 : : const char *passphrase,
1011 : : DissectImageFlags flags,
1012 : : DecryptedImage *d) {
1013 : :
1014 : 0 : _cleanup_free_ char *node = NULL, *name = NULL;
1015 : 0 : _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
1016 : : int r;
1017 : :
1018 [ # # ]: 0 : assert(m);
1019 [ # # ]: 0 : assert(d);
1020 : :
1021 [ # # # # : 0 : if (!m->found || !m->node || !m->fstype)
# # ]
1022 : 0 : return 0;
1023 : :
1024 [ # # ]: 0 : if (!streq(m->fstype, "crypto_LUKS"))
1025 : 0 : return 0;
1026 : :
1027 [ # # ]: 0 : if (!passphrase)
1028 : 0 : return -ENOKEY;
1029 : :
1030 : 0 : r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
1031 [ # # ]: 0 : if (r < 0)
1032 : 0 : return r;
1033 : :
1034 [ # # ]: 0 : if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
1035 : 0 : return -ENOMEM;
1036 : :
1037 : 0 : r = crypt_init(&cd, m->node);
1038 [ # # ]: 0 : if (r < 0)
1039 [ # # ]: 0 : return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
1040 : :
1041 : 0 : r = crypt_load(cd, CRYPT_LUKS, NULL);
1042 [ # # ]: 0 : if (r < 0)
1043 [ # # ]: 0 : return log_debug_errno(r, "Failed to load LUKS metadata: %m");
1044 : :
1045 : 0 : r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
1046 : : ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
1047 : : ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
1048 [ # # ]: 0 : if (r < 0) {
1049 [ # # ]: 0 : log_debug_errno(r, "Failed to activate LUKS device: %m");
1050 [ # # ]: 0 : return r == -EPERM ? -EKEYREJECTED : r;
1051 : : }
1052 : :
1053 : 0 : d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
1054 : 0 : d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
1055 : 0 : d->n_decrypted++;
1056 : :
1057 : 0 : m->decrypted_node = TAKE_PTR(node);
1058 : :
1059 : 0 : return 0;
1060 : : }
1061 : :
1062 : 0 : static int verity_partition(
1063 : : DissectedPartition *m,
1064 : : DissectedPartition *v,
1065 : : const void *root_hash,
1066 : : size_t root_hash_size,
1067 : : DissectImageFlags flags,
1068 : : DecryptedImage *d) {
1069 : :
1070 : 0 : _cleanup_free_ char *node = NULL, *name = NULL;
1071 : 0 : _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
1072 : : int r;
1073 : :
1074 [ # # ]: 0 : assert(m);
1075 [ # # ]: 0 : assert(v);
1076 : :
1077 [ # # ]: 0 : if (!root_hash)
1078 : 0 : return 0;
1079 : :
1080 [ # # # # : 0 : if (!m->found || !m->node || !m->fstype)
# # ]
1081 : 0 : return 0;
1082 [ # # # # : 0 : if (!v->found || !v->node || !v->fstype)
# # ]
1083 : 0 : return 0;
1084 : :
1085 [ # # ]: 0 : if (!streq(v->fstype, "DM_verity_hash"))
1086 : 0 : return 0;
1087 : :
1088 : 0 : r = make_dm_name_and_node(m->node, "-verity", &name, &node);
1089 [ # # ]: 0 : if (r < 0)
1090 : 0 : return r;
1091 : :
1092 [ # # ]: 0 : if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
1093 : 0 : return -ENOMEM;
1094 : :
1095 : 0 : r = crypt_init(&cd, v->node);
1096 [ # # ]: 0 : if (r < 0)
1097 : 0 : return r;
1098 : :
1099 : 0 : r = crypt_load(cd, CRYPT_VERITY, NULL);
1100 [ # # ]: 0 : if (r < 0)
1101 : 0 : return r;
1102 : :
1103 : 0 : r = crypt_set_data_device(cd, m->node);
1104 [ # # ]: 0 : if (r < 0)
1105 : 0 : return r;
1106 : :
1107 : 0 : r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
1108 [ # # ]: 0 : if (r < 0)
1109 : 0 : return r;
1110 : :
1111 : 0 : d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
1112 : 0 : d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
1113 : 0 : d->n_decrypted++;
1114 : :
1115 : 0 : m->decrypted_node = TAKE_PTR(node);
1116 : :
1117 : 0 : return 0;
1118 : : }
1119 : : #endif
1120 : :
1121 : 0 : int dissected_image_decrypt(
1122 : : DissectedImage *m,
1123 : : const char *passphrase,
1124 : : const void *root_hash,
1125 : : size_t root_hash_size,
1126 : : DissectImageFlags flags,
1127 : : DecryptedImage **ret) {
1128 : :
1129 : : #if HAVE_LIBCRYPTSETUP
1130 : 0 : _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
1131 : : unsigned i;
1132 : : int r;
1133 : : #endif
1134 : :
1135 [ # # ]: 0 : assert(m);
1136 [ # # # # ]: 0 : assert(root_hash || root_hash_size == 0);
1137 : :
1138 : : /* Returns:
1139 : : *
1140 : : * = 0 → There was nothing to decrypt
1141 : : * > 0 → Decrypted successfully
1142 : : * -ENOKEY → There's something to decrypt but no key was supplied
1143 : : * -EKEYREJECTED → Passed key was not correct
1144 : : */
1145 : :
1146 [ # # # # ]: 0 : if (root_hash && root_hash_size < sizeof(sd_id128_t))
1147 : 0 : return -EINVAL;
1148 : :
1149 [ # # # # ]: 0 : if (!m->encrypted && !m->verity) {
1150 : 0 : *ret = NULL;
1151 : 0 : return 0;
1152 : : }
1153 : :
1154 : : #if HAVE_LIBCRYPTSETUP
1155 : 0 : d = new0(DecryptedImage, 1);
1156 [ # # ]: 0 : if (!d)
1157 : 0 : return -ENOMEM;
1158 : :
1159 [ # # ]: 0 : for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
1160 : 0 : DissectedPartition *p = m->partitions + i;
1161 : : int k;
1162 : :
1163 [ # # ]: 0 : if (!p->found)
1164 : 0 : continue;
1165 : :
1166 : 0 : r = decrypt_partition(p, passphrase, flags, d);
1167 [ # # ]: 0 : if (r < 0)
1168 : 0 : return r;
1169 : :
1170 : 0 : k = PARTITION_VERITY_OF(i);
1171 [ # # ]: 0 : if (k >= 0) {
1172 : 0 : r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
1173 [ # # ]: 0 : if (r < 0)
1174 : 0 : return r;
1175 : : }
1176 : :
1177 [ # # # # ]: 0 : if (!p->decrypted_fstype && p->decrypted_node) {
1178 : 0 : r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
1179 [ # # # # ]: 0 : if (r < 0 && r != -EUCLEAN)
1180 : 0 : return r;
1181 : : }
1182 : : }
1183 : :
1184 : 0 : *ret = TAKE_PTR(d);
1185 : :
1186 : 0 : return 1;
1187 : : #else
1188 : : return -EOPNOTSUPP;
1189 : : #endif
1190 : : }
1191 : :
1192 : 0 : int dissected_image_decrypt_interactively(
1193 : : DissectedImage *m,
1194 : : const char *passphrase,
1195 : : const void *root_hash,
1196 : : size_t root_hash_size,
1197 : : DissectImageFlags flags,
1198 : : DecryptedImage **ret) {
1199 : :
1200 : 0 : _cleanup_strv_free_erase_ char **z = NULL;
1201 : 0 : int n = 3, r;
1202 : :
1203 [ # # ]: 0 : if (passphrase)
1204 : 0 : n--;
1205 : :
1206 : : for (;;) {
1207 : 0 : r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
1208 [ # # ]: 0 : if (r >= 0)
1209 : 0 : return r;
1210 [ # # ]: 0 : if (r == -EKEYREJECTED)
1211 [ # # ]: 0 : log_error_errno(r, "Incorrect passphrase, try again!");
1212 [ # # ]: 0 : else if (r != -ENOKEY)
1213 [ # # ]: 0 : return log_error_errno(r, "Failed to decrypt image: %m");
1214 : :
1215 [ # # ]: 0 : if (--n < 0)
1216 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED),
1217 : : "Too many retries.");
1218 : :
1219 : 0 : z = strv_free(z);
1220 : :
1221 : 0 : r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
1222 [ # # ]: 0 : if (r < 0)
1223 [ # # ]: 0 : return log_error_errno(r, "Failed to query for passphrase: %m");
1224 : :
1225 : 0 : passphrase = z[0];
1226 : : }
1227 : : }
1228 : :
1229 : 0 : int decrypted_image_relinquish(DecryptedImage *d) {
1230 : :
1231 : : #if HAVE_LIBCRYPTSETUP
1232 : : size_t i;
1233 : : int r;
1234 : : #endif
1235 : :
1236 [ # # ]: 0 : assert(d);
1237 : :
1238 : : /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
1239 : : * that we don't clean it up ourselves either anymore */
1240 : :
1241 : : #if HAVE_LIBCRYPTSETUP
1242 [ # # ]: 0 : for (i = 0; i < d->n_decrypted; i++) {
1243 : 0 : DecryptedPartition *p = d->decrypted + i;
1244 : :
1245 [ # # ]: 0 : if (p->relinquished)
1246 : 0 : continue;
1247 : :
1248 : 0 : r = dm_deferred_remove(p->name);
1249 [ # # ]: 0 : if (r < 0)
1250 [ # # ]: 0 : return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
1251 : :
1252 : 0 : p->relinquished = true;
1253 : : }
1254 : : #endif
1255 : :
1256 : 0 : return 0;
1257 : : }
1258 : :
1259 : 0 : int root_hash_load(const char *image, void **ret, size_t *ret_size) {
1260 : 0 : _cleanup_free_ char *text = NULL;
1261 : 0 : _cleanup_free_ void *k = NULL;
1262 : : size_t l;
1263 : : int r;
1264 : :
1265 [ # # ]: 0 : assert(image);
1266 [ # # ]: 0 : assert(ret);
1267 [ # # ]: 0 : assert(ret_size);
1268 : :
1269 [ # # ]: 0 : if (is_device_path(image)) {
1270 : : /* If we are asked to load the root hash for a device node, exit early */
1271 : 0 : *ret = NULL;
1272 : 0 : *ret_size = 0;
1273 : 0 : return 0;
1274 : : }
1275 : :
1276 : 0 : r = getxattr_malloc(image, "user.verity.roothash", &text, true);
1277 [ # # ]: 0 : if (r < 0) {
1278 : : char *fn, *e, *n;
1279 : :
1280 [ # # # # ]: 0 : if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
1281 : 0 : return r;
1282 : :
1283 [ # # # # ]: 0 : fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
1284 : 0 : n = stpcpy(fn, image);
1285 : 0 : e = endswith(fn, ".raw");
1286 [ # # ]: 0 : if (e)
1287 : 0 : n = e;
1288 : :
1289 : 0 : strcpy(n, ".roothash");
1290 : :
1291 : 0 : r = read_one_line_file(fn, &text);
1292 [ # # ]: 0 : if (r == -ENOENT) {
1293 : 0 : *ret = NULL;
1294 : 0 : *ret_size = 0;
1295 : 0 : return 0;
1296 : : }
1297 [ # # ]: 0 : if (r < 0)
1298 : 0 : return r;
1299 : : }
1300 : :
1301 : 0 : r = unhexmem(text, strlen(text), &k, &l);
1302 [ # # ]: 0 : if (r < 0)
1303 : 0 : return r;
1304 [ # # ]: 0 : if (l < sizeof(sd_id128_t))
1305 : 0 : return -EINVAL;
1306 : :
1307 : 0 : *ret = TAKE_PTR(k);
1308 : 0 : *ret_size = l;
1309 : :
1310 : 0 : return 1;
1311 : : }
1312 : :
1313 : 0 : int dissected_image_acquire_metadata(DissectedImage *m) {
1314 : :
1315 : : enum {
1316 : : META_HOSTNAME,
1317 : : META_MACHINE_ID,
1318 : : META_MACHINE_INFO,
1319 : : META_OS_RELEASE,
1320 : : _META_MAX,
1321 : : };
1322 : :
1323 : : static const char *const paths[_META_MAX] = {
1324 : : [META_HOSTNAME] = "/etc/hostname\0",
1325 : : [META_MACHINE_ID] = "/etc/machine-id\0",
1326 : : [META_MACHINE_INFO] = "/etc/machine-info\0",
1327 : : [META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0",
1328 : : };
1329 : :
1330 : 0 : _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
1331 : 0 : _cleanup_(rmdir_and_freep) char *t = NULL;
1332 : 0 : _cleanup_(sigkill_waitp) pid_t child = 0;
1333 : 0 : sd_id128_t machine_id = SD_ID128_NULL;
1334 : 0 : _cleanup_free_ char *hostname = NULL;
1335 : 0 : unsigned n_meta_initialized = 0, k;
1336 : : int fds[2 * _META_MAX], r;
1337 : :
1338 [ # # ]: 0 : BLOCK_SIGNALS(SIGCHLD);
1339 : :
1340 [ # # ]: 0 : assert(m);
1341 : :
1342 [ # # ]: 0 : for (; n_meta_initialized < _META_MAX; n_meta_initialized ++)
1343 [ # # ]: 0 : if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
1344 : 0 : r = -errno;
1345 : 0 : goto finish;
1346 : : }
1347 : :
1348 : 0 : r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
1349 [ # # ]: 0 : if (r < 0)
1350 : 0 : goto finish;
1351 : :
1352 : 0 : r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
1353 [ # # ]: 0 : if (r < 0)
1354 : 0 : goto finish;
1355 [ # # ]: 0 : if (r == 0) {
1356 : 0 : r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
1357 [ # # ]: 0 : if (r < 0) {
1358 [ # # ]: 0 : log_debug_errno(r, "Failed to mount dissected image: %m");
1359 : 0 : _exit(EXIT_FAILURE);
1360 : : }
1361 : :
1362 [ # # ]: 0 : for (k = 0; k < _META_MAX; k++) {
1363 [ # # ]: 0 : _cleanup_close_ int fd = -1;
1364 : : const char *p;
1365 : :
1366 : 0 : fds[2*k] = safe_close(fds[2*k]);
1367 : :
1368 [ # # # # ]: 0 : NULSTR_FOREACH(p, paths[k]) {
1369 : 0 : fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
1370 [ # # ]: 0 : if (fd >= 0)
1371 : 0 : break;
1372 : : }
1373 [ # # ]: 0 : if (fd < 0) {
1374 [ # # ]: 0 : log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
1375 : 0 : continue;
1376 : : }
1377 : :
1378 : 0 : r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
1379 [ # # ]: 0 : if (r < 0)
1380 : 0 : _exit(EXIT_FAILURE);
1381 : :
1382 : 0 : fds[2*k+1] = safe_close(fds[2*k+1]);
1383 : : }
1384 : :
1385 : 0 : _exit(EXIT_SUCCESS);
1386 : : }
1387 : :
1388 [ # # ]: 0 : for (k = 0; k < _META_MAX; k++) {
1389 [ # # ]: 0 : _cleanup_fclose_ FILE *f = NULL;
1390 : :
1391 : 0 : fds[2*k+1] = safe_close(fds[2*k+1]);
1392 : :
1393 : 0 : f = fdopen(fds[2*k], "r");
1394 [ # # ]: 0 : if (!f) {
1395 : 0 : r = -errno;
1396 : 0 : goto finish;
1397 : : }
1398 : :
1399 : 0 : fds[2*k] = -1;
1400 : :
1401 [ # # # # : 0 : switch (k) {
# ]
1402 : :
1403 : 0 : case META_HOSTNAME:
1404 : 0 : r = read_etc_hostname_stream(f, &hostname);
1405 [ # # ]: 0 : if (r < 0)
1406 [ # # ]: 0 : log_debug_errno(r, "Failed to read /etc/hostname: %m");
1407 : :
1408 : 0 : break;
1409 : :
1410 : 0 : case META_MACHINE_ID: {
1411 : 0 : _cleanup_free_ char *line = NULL;
1412 : :
1413 : 0 : r = read_line(f, LONG_LINE_MAX, &line);
1414 [ # # ]: 0 : if (r < 0)
1415 [ # # ]: 0 : log_debug_errno(r, "Failed to read /etc/machine-id: %m");
1416 [ # # ]: 0 : else if (r == 33) {
1417 : 0 : r = sd_id128_from_string(line, &machine_id);
1418 [ # # ]: 0 : if (r < 0)
1419 [ # # ]: 0 : log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
1420 [ # # ]: 0 : } else if (r == 0)
1421 [ # # ]: 0 : log_debug("/etc/machine-id file is empty.");
1422 : : else
1423 [ # # ]: 0 : log_debug("/etc/machine-id has unexpected length %i.", r);
1424 : :
1425 : 0 : break;
1426 : : }
1427 : :
1428 : 0 : case META_MACHINE_INFO:
1429 : 0 : r = load_env_file_pairs(f, "machine-info", &machine_info);
1430 [ # # ]: 0 : if (r < 0)
1431 [ # # ]: 0 : log_debug_errno(r, "Failed to read /etc/machine-info: %m");
1432 : :
1433 : 0 : break;
1434 : :
1435 : 0 : case META_OS_RELEASE:
1436 : 0 : r = load_env_file_pairs(f, "os-release", &os_release);
1437 [ # # ]: 0 : if (r < 0)
1438 [ # # ]: 0 : log_debug_errno(r, "Failed to read OS release file: %m");
1439 : :
1440 : 0 : break;
1441 : : }
1442 : 0 : }
1443 : :
1444 : 0 : r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
1445 : 0 : child = 0;
1446 [ # # ]: 0 : if (r < 0)
1447 : 0 : goto finish;
1448 [ # # ]: 0 : if (r != EXIT_SUCCESS)
1449 : 0 : return -EPROTO;
1450 : :
1451 : 0 : free_and_replace(m->hostname, hostname);
1452 : 0 : m->machine_id = machine_id;
1453 : 0 : strv_free_and_replace(m->machine_info, machine_info);
1454 : 0 : strv_free_and_replace(m->os_release, os_release);
1455 : :
1456 : 0 : finish:
1457 [ # # ]: 0 : for (k = 0; k < n_meta_initialized; k++)
1458 : 0 : safe_close_pair(fds + 2*k);
1459 : :
1460 : 0 : return r;
1461 : : }
1462 : :
1463 : 0 : int dissect_image_and_warn(
1464 : : int fd,
1465 : : const char *name,
1466 : : const void *root_hash,
1467 : : size_t root_hash_size,
1468 : : DissectImageFlags flags,
1469 : : DissectedImage **ret) {
1470 : :
1471 : 0 : _cleanup_free_ char *buffer = NULL;
1472 : : int r;
1473 : :
1474 [ # # ]: 0 : if (!name) {
1475 : 0 : r = fd_get_path(fd, &buffer);
1476 [ # # ]: 0 : if (r < 0)
1477 : 0 : return r;
1478 : :
1479 : 0 : name = buffer;
1480 : : }
1481 : :
1482 : 0 : r = dissect_image(fd, root_hash, root_hash_size, flags, ret);
1483 : :
1484 [ # # # # : 0 : switch (r) {
# # # ]
1485 : :
1486 : 0 : case -EOPNOTSUPP:
1487 [ # # ]: 0 : return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
1488 : :
1489 : 0 : case -ENOPKG:
1490 [ # # ]: 0 : return log_error_errno(r, "Couldn't identify a suitable partition table or file system in '%s'.", name);
1491 : :
1492 : 0 : case -EADDRNOTAVAIL:
1493 [ # # ]: 0 : return log_error_errno(r, "No root partition for specified root hash found in '%s'.", name);
1494 : :
1495 : 0 : case -ENOTUNIQ:
1496 [ # # ]: 0 : return log_error_errno(r, "Multiple suitable root partitions found in image '%s'.", name);
1497 : :
1498 : 0 : case -ENXIO:
1499 [ # # ]: 0 : return log_error_errno(r, "No suitable root partition found in image '%s'.", name);
1500 : :
1501 : 0 : case -EPROTONOSUPPORT:
1502 [ # # ]: 0 : return log_error_errno(r, "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", name);
1503 : :
1504 : 0 : default:
1505 [ # # ]: 0 : if (r < 0)
1506 [ # # ]: 0 : return log_error_errno(r, "Failed to dissect image '%s': %m", name);
1507 : :
1508 : 0 : return r;
1509 : : }
1510 : : }
1511 : :
1512 : : static const char *const partition_designator_table[] = {
1513 : : [PARTITION_ROOT] = "root",
1514 : : [PARTITION_ROOT_SECONDARY] = "root-secondary",
1515 : : [PARTITION_HOME] = "home",
1516 : : [PARTITION_SRV] = "srv",
1517 : : [PARTITION_ESP] = "esp",
1518 : : [PARTITION_XBOOTLDR] = "xbootldr",
1519 : : [PARTITION_SWAP] = "swap",
1520 : : [PARTITION_ROOT_VERITY] = "root-verity",
1521 : : [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
1522 : : };
1523 : :
1524 [ + + + + ]: 88 : DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);
|