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