]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dissect-image.c
dissect: return the GPT partition UUID, too
[thirdparty/systemd.git] / src / shared / dissect-image.c
CommitLineData
8c1be37e
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
18b5886e
LP
20#ifdef HAVE_LIBCRYPTSETUP
21#include <libcryptsetup.h>
22#endif
23#include <linux/dm-ioctl.h>
8c1be37e
LP
24#include <sys/mount.h>
25
26#include "architecture.h"
18b5886e 27#include "ask-password-api.h"
8c1be37e
LP
28#include "blkid-util.h"
29#include "dissect-image.h"
18b5886e 30#include "fd-util.h"
8c1be37e
LP
31#include "gpt.h"
32#include "mount-util.h"
33#include "path-util.h"
34#include "stat-util.h"
18b5886e 35#include "stdio-util.h"
8c1be37e
LP
36#include "string-table.h"
37#include "string-util.h"
38#include "udev-util.h"
39
18b5886e
LP
40static int probe_filesystem(const char *node, char **ret_fstype) {
41#ifdef HAVE_BLKID
42 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
43 const char *fstype;
44 int r;
45
46 b = blkid_new_probe_from_filename(node);
47 if (!b)
48 return -ENOMEM;
49
50 blkid_probe_enable_superblocks(b, 1);
51 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
52
53 errno = 0;
54 r = blkid_do_safeprobe(b);
55 if (r == -2 || r == 1) {
56 log_debug("Failed to identify any partition type on partition %s", node);
57 goto not_found;
58 }
59 if (r != 0) {
60 if (errno == 0)
61 return -EIO;
62
63 return -errno;
64 }
65
66 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
67
68 if (fstype) {
69 char *t;
70
71 t = strdup(fstype);
72 if (!t)
73 return -ENOMEM;
74
75 *ret_fstype = t;
76 return 1;
77 }
78
79not_found:
80 *ret_fstype = NULL;
81 return 0;
82#else
83 return -EOPNOTSUPP;
84#endif
85}
86
9b6deb03 87int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
8c1be37e
LP
88
89#ifdef HAVE_BLKID
4623e8e6 90 sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
8c1be37e
LP
91 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
92 bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
93 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
94 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
95 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
96 _cleanup_udev_unref_ struct udev *udev = NULL;
97 _cleanup_free_ char *generic_node = NULL;
be30ad41 98 sd_id128_t generic_uuid = SD_ID128_NULL;
9b6deb03 99 const char *pttype = NULL;
8c1be37e
LP
100 struct udev_list_entry *first, *item;
101 blkid_partlist pl;
102 int r, generic_nr;
103 struct stat st;
104 unsigned i;
105
106 assert(fd >= 0);
107 assert(ret);
4623e8e6 108 assert(root_hash || root_hash_size == 0);
8c1be37e
LP
109
110 /* Probes a disk image, and returns information about what it found in *ret.
111 *
4623e8e6
LP
112 * Returns -ENOPKG if no suitable partition table or file system could be found.
113 * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
114
115 if (root_hash) {
116 /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
117 * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
118 * 128bit. */
119
120 if (root_hash_size < sizeof(sd_id128_t))
121 return -EINVAL;
122
123 memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
124 memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
125
126 if (sd_id128_is_null(root_uuid))
127 return -EINVAL;
128 if (sd_id128_is_null(verity_uuid))
129 return -EINVAL;
130 }
8c1be37e
LP
131
132 if (fstat(fd, &st) < 0)
133 return -errno;
134
135 if (!S_ISBLK(st.st_mode))
136 return -ENOTBLK;
137
138 b = blkid_new_probe();
139 if (!b)
140 return -ENOMEM;
141
142 errno = 0;
143 r = blkid_probe_set_device(b, fd, 0, 0);
144 if (r != 0) {
145 if (errno == 0)
146 return -ENOMEM;
147
148 return -errno;
149 }
150
9b6deb03
LP
151 if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
152 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
153 blkid_probe_enable_superblocks(b, 1);
154 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
155 }
156
8c1be37e
LP
157 blkid_probe_enable_partitions(b, 1);
158 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
159
160 errno = 0;
161 r = blkid_do_safeprobe(b);
162 if (r == -2 || r == 1) {
163 log_debug("Failed to identify any partition table.");
164 return -ENOPKG;
165 }
166 if (r != 0) {
167 if (errno == 0)
168 return -EIO;
169
170 return -errno;
171 }
172
173 m = new0(DissectedImage, 1);
174 if (!m)
175 return -ENOMEM;
176
9b6deb03
LP
177 if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
178 const char *usage = NULL;
8c1be37e 179
9b6deb03
LP
180 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
181 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
182 _cleanup_free_ char *t = NULL, *n = NULL;
183 const char *fstype = NULL;
8c1be37e 184
9b6deb03
LP
185 /* OK, we have found a file system, that's our root partition then. */
186 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
8c1be37e 187
9b6deb03
LP
188 if (fstype) {
189 t = strdup(fstype);
190 if (!t)
191 return -ENOMEM;
192 }
193
194 if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
195 return -ENOMEM;
8c1be37e 196
9b6deb03
LP
197 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
198 .found = true,
199 .rw = true,
200 .partno = -1,
201 .architecture = _ARCHITECTURE_INVALID,
202 .fstype = t,
203 .node = n,
204 };
8c1be37e 205
9b6deb03 206 t = n = NULL;
8c1be37e 207
9b6deb03 208 m->encrypted = streq(fstype, "crypto_LUKS");
18b5886e 209
9b6deb03
LP
210 *ret = m;
211 m = NULL;
8c1be37e 212
9b6deb03
LP
213 return 0;
214 }
8c1be37e
LP
215 }
216
217 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
218 if (!pttype)
219 return -ENOPKG;
220
221 is_gpt = streq_ptr(pttype, "gpt");
222 is_mbr = streq_ptr(pttype, "dos");
223
9b6deb03 224 if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
8c1be37e
LP
225 return -ENOPKG;
226
227 errno = 0;
228 pl = blkid_probe_get_partitions(b);
229 if (!pl) {
230 if (errno == 0)
231 return -ENOMEM;
232
233 return -errno;
234 }
235
236 udev = udev_new();
237 if (!udev)
238 return -errno;
239
240 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
241 if (!d)
242 return -ENOMEM;
243
244 for (i = 0;; i++) {
245 int n, z;
246
247 if (i >= 10) {
248 log_debug("Kernel partitions never appeared.");
249 return -ENXIO;
250 }
251
252 e = udev_enumerate_new(udev);
253 if (!e)
254 return -errno;
255
256 r = udev_enumerate_add_match_parent(e, d);
257 if (r < 0)
258 return r;
259
260 r = udev_enumerate_scan_devices(e);
261 if (r < 0)
262 return r;
263
264 /* Count the partitions enumerated by the kernel */
265 n = 0;
266 first = udev_enumerate_get_list_entry(e);
267 udev_list_entry_foreach(item, first)
268 n++;
269
270 /* Count the partitions enumerated by blkid */
271 z = blkid_partlist_numof_partitions(pl);
272 if (n == z + 1)
273 break;
274 if (n > z + 1) {
275 log_debug("blkid and kernel partition list do not match.");
276 return -EIO;
277 }
278 if (n < z + 1) {
279 unsigned j;
280
281 /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
282 * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
283 * synchronous call that waits until probing is complete. */
284
285 for (j = 0; j < 20; j++) {
286
287 r = ioctl(fd, BLKRRPART, 0);
288 if (r < 0)
289 r = -errno;
290 if (r >= 0 || r != -EBUSY)
291 break;
292
293 /* If something else has the device open, such as an udev rule, the ioctl will return
294 * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
295 * bit, and try again.
296 *
297 * This is really something they should fix in the kernel! */
298
299 usleep(50 * USEC_PER_MSEC);
300 }
301
302 if (r < 0)
303 return r;
304 }
305
306 e = udev_enumerate_unref(e);
307 }
308
309 first = udev_enumerate_get_list_entry(e);
310 udev_list_entry_foreach(item, first) {
311 _cleanup_udev_device_unref_ struct udev_device *q;
9b6deb03 312 unsigned long long pflags;
8c1be37e
LP
313 blkid_partition pp;
314 const char *node;
315 dev_t qn;
316 int nr;
317
318 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
319 if (!q)
320 return -errno;
321
322 qn = udev_device_get_devnum(q);
323 if (major(qn) == 0)
324 continue;
325
326 if (st.st_rdev == qn)
327 continue;
328
329 node = udev_device_get_devnode(q);
330 if (!node)
331 continue;
332
333 pp = blkid_partlist_devno_to_partition(pl, qn);
334 if (!pp)
335 continue;
336
9b6deb03 337 pflags = blkid_partition_get_flags(pp);
8c1be37e
LP
338
339 nr = blkid_partition_get_partno(pp);
340 if (nr < 0)
341 continue;
342
343 if (is_gpt) {
344 int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
4623e8e6
LP
345 const char *stype, *sid, *fstype = NULL;
346 sd_id128_t type_id, id;
8c1be37e
LP
347 bool rw = true;
348
9b6deb03 349 if (pflags & GPT_FLAG_NO_AUTO)
8c1be37e
LP
350 continue;
351
4623e8e6
LP
352 sid = blkid_partition_get_uuid(pp);
353 if (!sid)
354 continue;
355 if (sd_id128_from_string(sid, &id) < 0)
356 continue;
357
8c1be37e
LP
358 stype = blkid_partition_get_type_string(pp);
359 if (!stype)
360 continue;
8c1be37e
LP
361 if (sd_id128_from_string(stype, &type_id) < 0)
362 continue;
363
364 if (sd_id128_equal(type_id, GPT_HOME)) {
365 designator = PARTITION_HOME;
9b6deb03 366 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e
LP
367 } else if (sd_id128_equal(type_id, GPT_SRV)) {
368 designator = PARTITION_SRV;
9b6deb03 369 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e
LP
370 } else if (sd_id128_equal(type_id, GPT_ESP)) {
371 designator = PARTITION_ESP;
372 fstype = "vfat";
373 }
374#ifdef GPT_ROOT_NATIVE
375 else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
4623e8e6
LP
376
377 /* If a root ID is specified, ignore everything but the root id */
378 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
379 continue;
380
8c1be37e
LP
381 designator = PARTITION_ROOT;
382 architecture = native_architecture();
9b6deb03 383 rw = !(pflags & GPT_FLAG_READ_ONLY);
4f8b86e3 384 } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
4623e8e6
LP
385
386 m->can_verity = true;
387
388 /* Ignore verity unless a root hash is specified */
389 if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
390 continue;
391
392 designator = PARTITION_ROOT_VERITY;
393 fstype = "DM_verity_hash";
394 architecture = native_architecture();
395 rw = false;
396 }
397#endif
8c1be37e
LP
398#ifdef GPT_ROOT_SECONDARY
399 else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
4623e8e6
LP
400
401 /* If a root ID is specified, ignore everything but the root id */
402 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
403 continue;
404
8c1be37e
LP
405 designator = PARTITION_ROOT_SECONDARY;
406 architecture = SECONDARY_ARCHITECTURE;
9b6deb03 407 rw = !(pflags & GPT_FLAG_READ_ONLY);
4f8b86e3 408 } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
4623e8e6
LP
409 m->can_verity = true;
410
411 /* Ignore verity unless root has is specified */
412 if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
413 continue;
414
415 designator = PARTITION_ROOT_SECONDARY_VERITY;
416 fstype = "DM_verity_hash";
417 architecture = SECONDARY_ARCHITECTURE;
418 rw = false;
419 }
8c1be37e
LP
420#endif
421 else if (sd_id128_equal(type_id, GPT_SWAP)) {
422 designator = PARTITION_SWAP;
423 fstype = "swap";
424 } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
425
426 if (generic_node)
427 multiple_generic = true;
428 else {
429 generic_nr = nr;
9b6deb03 430 generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
be30ad41 431 generic_uuid = id;
8c1be37e
LP
432 generic_node = strdup(node);
433 if (!generic_node)
434 return -ENOMEM;
435 }
436 }
437
438 if (designator != _PARTITION_DESIGNATOR_INVALID) {
439 _cleanup_free_ char *t = NULL, *n = NULL;
440
441 /* First one wins */
442 if (m->partitions[designator].found)
443 continue;
444
445 if (fstype) {
446 t = strdup(fstype);
447 if (!t)
448 return -ENOMEM;
449 }
450
451 n = strdup(node);
452 if (!n)
453 return -ENOMEM;
454
455 m->partitions[designator] = (DissectedPartition) {
456 .found = true,
457 .partno = nr,
458 .rw = rw,
459 .architecture = architecture,
460 .node = n,
461 .fstype = t,
be30ad41 462 .uuid = id,
8c1be37e
LP
463 };
464
465 n = t = NULL;
466 }
467
468 } else if (is_mbr) {
469
9b6deb03 470 if (pflags != 0x80) /* Bootable flag */
8c1be37e
LP
471 continue;
472
473 if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
474 continue;
475
476 if (generic_node)
477 multiple_generic = true;
478 else {
479 generic_nr = nr;
480 generic_rw = true;
481 generic_node = strdup(node);
482 if (!generic_node)
483 return -ENOMEM;
484 }
485 }
486 }
487
488 if (!m->partitions[PARTITION_ROOT].found) {
489 /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
490 * either, then check if there's a single generic one, and use that. */
491
4623e8e6
LP
492 if (m->partitions[PARTITION_ROOT_VERITY].found)
493 return -ENXIO;
494
8c1be37e
LP
495 if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
496 m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
497 zero(m->partitions[PARTITION_ROOT_SECONDARY]);
4623e8e6
LP
498
499 m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
500 zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
501
502 } else if (generic_node && !root_hash) {
8c1be37e
LP
503
504 if (multiple_generic)
505 return -ENOTUNIQ;
506
507 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
508 .found = true,
509 .rw = generic_rw,
510 .partno = generic_nr,
511 .architecture = _ARCHITECTURE_INVALID,
512 .node = generic_node,
be30ad41 513 .uuid = generic_uuid,
8c1be37e
LP
514 };
515
516 generic_node = NULL;
517 } else
518 return -ENXIO;
519 }
520
4623e8e6
LP
521 assert(m->partitions[PARTITION_ROOT].found);
522
523 if (root_hash) {
524 if (!m->partitions[PARTITION_ROOT_VERITY].found)
525 return -EADDRNOTAVAIL;
526
527 /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
528 * (which would be weird, after all the root hash should only be assigned to one pair of
529 * partitions... */
530 m->partitions[PARTITION_ROOT_SECONDARY].found = false;
531 m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
532
533 /* If we found a verity setup, then the root partition is necessarily read-only. */
534 m->partitions[PARTITION_ROOT].rw = false;
535
536 m->verity = true;
537 }
538
18b5886e
LP
539 blkid_free_probe(b);
540 b = NULL;
541
8c1be37e
LP
542 /* Fill in file system types if we don't know them yet. */
543 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
18b5886e 544 DissectedPartition *p = m->partitions + i;
8c1be37e 545
18b5886e 546 if (!p->found)
8c1be37e
LP
547 continue;
548
18b5886e
LP
549 if (!p->fstype && p->node) {
550 r = probe_filesystem(p->node, &p->fstype);
551 if (r < 0)
552 return r;
8c1be37e
LP
553 }
554
18b5886e
LP
555 if (streq_ptr(p->fstype, "crypto_LUKS"))
556 m->encrypted = true;
8c1be37e
LP
557 }
558
559 *ret = m;
560 m = NULL;
561
562 return 0;
563#else
564 return -EOPNOTSUPP;
565#endif
566}
567
568DissectedImage* dissected_image_unref(DissectedImage *m) {
569 unsigned i;
570
571 if (!m)
572 return NULL;
573
574 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
575 free(m->partitions[i].fstype);
576 free(m->partitions[i].node);
18b5886e
LP
577 free(m->partitions[i].decrypted_fstype);
578 free(m->partitions[i].decrypted_node);
8c1be37e
LP
579 }
580
581 free(m);
582 return NULL;
583}
584
18b5886e
LP
585static int is_loop_device(const char *path) {
586 char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
587 struct stat st;
588
589 assert(path);
590
591 if (stat(path, &st) < 0)
592 return -errno;
593
594 if (!S_ISBLK(st.st_mode))
595 return -ENOTBLK;
596
597 xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
598 if (access(s, F_OK) < 0) {
599 if (errno != ENOENT)
600 return -errno;
601
602 /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
603 xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
604 if (access(s, F_OK) < 0)
605 return errno == ENOENT ? false : -errno;
606 }
607
608 return true;
609}
610
611static int mount_partition(
612 DissectedPartition *m,
613 const char *where,
614 const char *directory,
615 DissectImageFlags flags) {
616
617 const char *p, *options = NULL, *node, *fstype;
8c1be37e
LP
618 bool rw;
619
620 assert(m);
621 assert(where);
622
18b5886e
LP
623 node = m->decrypted_node ?: m->node;
624 fstype = m->decrypted_fstype ?: m->fstype;
625
626 if (!m->found || !node || !fstype)
8c1be37e
LP
627 return 0;
628
18b5886e
LP
629 /* Stacked encryption? Yuck */
630 if (streq_ptr(fstype, "crypto_LUKS"))
631 return -ELOOP;
632
633 rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
8c1be37e
LP
634
635 if (directory)
636 p = strjoina(where, directory);
637 else
638 p = where;
639
18b5886e
LP
640 /* If requested, turn on discard support. */
641 if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs") &&
642 ((flags & DISSECT_IMAGE_DISCARD) ||
643 ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
644 options = "discard";
8c1be37e 645
18b5886e 646 return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
8c1be37e
LP
647}
648
18b5886e 649int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
8c1be37e
LP
650 int r;
651
652 assert(m);
653 assert(where);
654
655 if (!m->partitions[PARTITION_ROOT].found)
656 return -ENXIO;
657
658 r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
659 if (r < 0)
660 return r;
661
662 r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
663 if (r < 0)
664 return r;
665
666 r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
667 if (r < 0)
668 return r;
669
670 if (m->partitions[PARTITION_ESP].found) {
671 const char *mp, *x;
672
673 /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
674
675 mp = "/efi";
676 x = strjoina(where, mp);
677 r = dir_is_empty(x);
678 if (r == -ENOENT) {
679 mp = "/boot";
680 x = strjoina(where, mp);
681 r = dir_is_empty(x);
682 }
683 if (r > 0) {
684 r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
685 if (r < 0)
686 return r;
687 }
688 }
689
690 return 0;
691}
692
18b5886e
LP
693#ifdef HAVE_LIBCRYPTSETUP
694typedef struct DecryptedPartition {
695 struct crypt_device *device;
696 char *name;
697 bool relinquished;
698} DecryptedPartition;
699
700struct DecryptedImage {
701 DecryptedPartition *decrypted;
702 size_t n_decrypted;
703 size_t n_allocated;
704};
705#endif
706
707DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
708#ifdef HAVE_LIBCRYPTSETUP
709 size_t i;
710 int r;
711
712 if (!d)
713 return NULL;
714
715 for (i = 0; i < d->n_decrypted; i++) {
716 DecryptedPartition *p = d->decrypted + i;
717
718 if (p->device && p->name && !p->relinquished) {
719 r = crypt_deactivate(p->device, p->name);
720 if (r < 0)
721 log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
722 }
723
724 if (p->device)
725 crypt_free(p->device);
726 free(p->name);
727 }
728
729 free(d);
730#endif
731 return NULL;
732}
733
734#ifdef HAVE_LIBCRYPTSETUP
4623e8e6
LP
735
736static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
737 _cleanup_free_ char *name = NULL, *node = NULL;
738 const char *base;
739
740 assert(original_node);
741 assert(suffix);
742 assert(ret_name);
743 assert(ret_node);
744
745 base = strrchr(original_node, '/');
746 if (!base)
747 return -EINVAL;
748 base++;
749 if (isempty(base))
750 return -EINVAL;
751
752 name = strjoin(base, suffix);
753 if (!name)
754 return -ENOMEM;
755 if (!filename_is_valid(name))
756 return -EINVAL;
757
758 node = strjoin(crypt_get_dir(), "/", name);
759 if (!node)
760 return -ENOMEM;
761
762 *ret_name = name;
763 *ret_node = node;
764
765 name = node = NULL;
766 return 0;
767}
768
18b5886e
LP
769static int decrypt_partition(
770 DissectedPartition *m,
771 const char *passphrase,
772 DissectImageFlags flags,
773 DecryptedImage *d) {
774
775 _cleanup_free_ char *node = NULL, *name = NULL;
776 struct crypt_device *cd;
18b5886e
LP
777 int r;
778
779 assert(m);
780 assert(d);
781
782 if (!m->found || !m->node || !m->fstype)
783 return 0;
784
785 if (!streq(m->fstype, "crypto_LUKS"))
786 return 0;
787
4623e8e6
LP
788 r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
789 if (r < 0)
790 return r;
18b5886e
LP
791
792 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
793 return -ENOMEM;
794
795 r = crypt_init(&cd, m->node);
796 if (r < 0)
797 return r;
798
799 r = crypt_load(cd, CRYPT_LUKS1, NULL);
800 if (r < 0)
801 goto fail;
802
803 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
804 ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
805 ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
806 if (r == -EPERM) {
807 r = -EKEYREJECTED;
808 goto fail;
809 }
810 if (r < 0)
811 goto fail;
812
813 d->decrypted[d->n_decrypted].name = name;
814 name = NULL;
815
816 d->decrypted[d->n_decrypted].device = cd;
817 d->n_decrypted++;
818
819 m->decrypted_node = node;
820 node = NULL;
821
822 return 0;
823
4623e8e6
LP
824fail:
825 crypt_free(cd);
826 return r;
827}
828
829static int verity_partition(
830 DissectedPartition *m,
831 DissectedPartition *v,
832 const void *root_hash,
833 size_t root_hash_size,
834 DissectImageFlags flags,
835 DecryptedImage *d) {
836
837 _cleanup_free_ char *node = NULL, *name = NULL;
838 struct crypt_device *cd;
839 int r;
840
841 assert(m);
842 assert(v);
843
844 if (!root_hash)
845 return 0;
846
847 if (!m->found || !m->node || !m->fstype)
848 return 0;
849 if (!v->found || !v->node || !v->fstype)
850 return 0;
851
852 if (!streq(v->fstype, "DM_verity_hash"))
853 return 0;
854
855 r = make_dm_name_and_node(m->node, "-verity", &name, &node);
856 if (r < 0)
857 return r;
858
859 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
860 return -ENOMEM;
861
862 r = crypt_init(&cd, v->node);
863 if (r < 0)
864 return r;
865
866 r = crypt_load(cd, CRYPT_VERITY, NULL);
867 if (r < 0)
868 goto fail;
869
870 r = crypt_set_data_device(cd, m->node);
871 if (r < 0)
872 goto fail;
873
874 r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
875 if (r < 0)
876 goto fail;
877
878 d->decrypted[d->n_decrypted].name = name;
879 name = NULL;
880
881 d->decrypted[d->n_decrypted].device = cd;
882 d->n_decrypted++;
883
884 m->decrypted_node = node;
885 node = NULL;
886
887 return 0;
888
18b5886e
LP
889fail:
890 crypt_free(cd);
891 return r;
892}
893#endif
894
895int dissected_image_decrypt(
896 DissectedImage *m,
897 const char *passphrase,
4623e8e6
LP
898 const void *root_hash,
899 size_t root_hash_size,
18b5886e
LP
900 DissectImageFlags flags,
901 DecryptedImage **ret) {
902
903 _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
904#ifdef HAVE_LIBCRYPTSETUP
905 unsigned i;
906 int r;
907#endif
908
909 assert(m);
4623e8e6 910 assert(root_hash || root_hash_size == 0);
18b5886e
LP
911
912 /* Returns:
913 *
914 * = 0 → There was nothing to decrypt
915 * > 0 → Decrypted successfully
916 * -ENOKEY → There's some to decrypt but no key was supplied
917 * -EKEYREJECTED → Passed key was not correct
918 */
919
4623e8e6
LP
920 if (root_hash && root_hash_size < sizeof(sd_id128_t))
921 return -EINVAL;
922
923 if (!m->encrypted && !m->verity) {
18b5886e
LP
924 *ret = NULL;
925 return 0;
926 }
927
928#ifdef HAVE_LIBCRYPTSETUP
4623e8e6 929 if (m->encrypted && !passphrase)
18b5886e
LP
930 return -ENOKEY;
931
932 d = new0(DecryptedImage, 1);
933 if (!d)
934 return -ENOMEM;
935
936 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
937 DissectedPartition *p = m->partitions + i;
4623e8e6 938 int k;
18b5886e
LP
939
940 if (!p->found)
941 continue;
942
943 r = decrypt_partition(p, passphrase, flags, d);
944 if (r < 0)
945 return r;
946
4623e8e6
LP
947 k = PARTITION_VERITY_OF(i);
948 if (k >= 0) {
949 r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
950 if (r < 0)
951 return r;
952 }
953
18b5886e
LP
954 if (!p->decrypted_fstype && p->decrypted_node) {
955 r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
956 if (r < 0)
957 return r;
958 }
959 }
960
961 *ret = d;
962 d = NULL;
963
964 return 1;
965#else
966 return -EOPNOTSUPP;
967#endif
968}
969
970int dissected_image_decrypt_interactively(
971 DissectedImage *m,
972 const char *passphrase,
4623e8e6
LP
973 const void *root_hash,
974 size_t root_hash_size,
18b5886e
LP
975 DissectImageFlags flags,
976 DecryptedImage **ret) {
977
978 _cleanup_strv_free_erase_ char **z = NULL;
979 int n = 3, r;
980
981 if (passphrase)
982 n--;
983
984 for (;;) {
4623e8e6 985 r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
18b5886e
LP
986 if (r >= 0)
987 return r;
988 if (r == -EKEYREJECTED)
989 log_error_errno(r, "Incorrect passphrase, try again!");
990 else if (r != -ENOKEY) {
991 log_error_errno(r, "Failed to decrypt image: %m");
992 return r;
993 }
994
995 if (--n < 0) {
996 log_error("Too many retries.");
997 return -EKEYREJECTED;
998 }
999
1000 z = strv_free(z);
1001
1002 r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
1003 if (r < 0)
1004 return log_error_errno(r, "Failed to query for passphrase: %m");
1005
1006 passphrase = z[0];
1007 }
1008}
1009
1010#ifdef HAVE_LIBCRYPTSETUP
1011static int deferred_remove(DecryptedPartition *p) {
1012
1013 struct dm_ioctl dm = {
1014 .version = {
1015 DM_VERSION_MAJOR,
1016 DM_VERSION_MINOR,
1017 DM_VERSION_PATCHLEVEL
1018 },
1019 .data_size = sizeof(dm),
1020 .flags = DM_DEFERRED_REMOVE,
1021 };
1022
1023 _cleanup_close_ int fd = -1;
1024
1025 assert(p);
1026
1027 /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
1028
1029 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
1030 if (fd < 0)
1031 return -errno;
1032
1033 strncpy(dm.name, p->name, sizeof(dm.name));
1034
1035 if (ioctl(fd, DM_DEV_REMOVE, &dm))
1036 return -errno;
1037
1038 return 0;
1039}
1040#endif
1041
1042int decrypted_image_relinquish(DecryptedImage *d) {
1043
1044#ifdef HAVE_LIBCRYPTSETUP
1045 size_t i;
1046 int r;
1047#endif
1048
1049 assert(d);
1050
1051 /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
1052 * that we don't clean it up ourselves either anymore */
1053
1054#ifdef HAVE_LIBCRYPTSETUP
1055 for (i = 0; i < d->n_decrypted; i++) {
1056 DecryptedPartition *p = d->decrypted + i;
1057
1058 if (p->relinquished)
1059 continue;
1060
1061 r = deferred_remove(p);
1062 if (r < 0)
1063 return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
1064
1065 p->relinquished = true;
1066 }
1067#endif
1068
1069 return 0;
1070}
1071
8c1be37e
LP
1072static const char *const partition_designator_table[] = {
1073 [PARTITION_ROOT] = "root",
1074 [PARTITION_ROOT_SECONDARY] = "root-secondary",
1075 [PARTITION_HOME] = "home",
1076 [PARTITION_SRV] = "srv",
1077 [PARTITION_ESP] = "esp",
1078 [PARTITION_SWAP] = "swap",
4623e8e6
LP
1079 [PARTITION_ROOT_VERITY] = "root-verity",
1080 [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
8c1be37e
LP
1081};
1082
1083DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);