]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/dissect-image.c
build-sys: use #if Y instead of #ifdef Y everywhere
[thirdparty/systemd.git] / src / shared / dissect-image.c
index 878cb008aa9b146fd4d278f45013d08e5d2f2c6b..dd8650e1a8b9bb96910232927bf601daf0e7e831 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
 #include <libcryptsetup.h>
 #endif
-#include <linux/dm-ioctl.h>
 #include <sys/mount.h>
 
 #include "architecture.h"
 #include "blkid-util.h"
 #include "dissect-image.h"
 #include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
 #include "gpt.h"
+#include "hexdecoct.h"
+#include "linux-3.13/dm-ioctl.h"
 #include "mount-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "strv.h"
 #include "udev-util.h"
+#include "xattr-util.h"
 
-static int probe_filesystem(const char *node, char **ret_fstype) {
-#ifdef HAVE_BLKID
+_unused_ static int probe_filesystem(const char *node, char **ret_fstype) {
+#if HAVE_BLKID
         _cleanup_blkid_free_probe_ blkid_probe b = NULL;
         const char *fstype;
         int r;
@@ -56,12 +61,8 @@ static int probe_filesystem(const char *node, char **ret_fstype) {
                 log_debug("Failed to identify any partition type on partition %s", node);
                 goto not_found;
         }
-        if (r != 0) {
-                if (errno == 0)
-                        return -EIO;
-
-                return -errno;
-        }
+        if (r != 0)
+                return -errno ?: -EIO;
 
         (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
 
@@ -86,7 +87,7 @@ not_found:
 
 int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
 
-#ifdef HAVE_BLKID
+#if HAVE_BLKID
         sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
         bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
@@ -141,12 +142,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
 
         errno = 0;
         r = blkid_probe_set_device(b, fd, 0, 0);
-        if (r != 0) {
-                if (errno == 0)
-                        return -ENOMEM;
-
-                return -errno;
-        }
+        if (r != 0)
+                return -errno ?: -ENOMEM;
 
         if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
                 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
@@ -163,12 +160,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 log_debug("Failed to identify any partition table.");
                 return -ENOPKG;
         }
-        if (r != 0) {
-                if (errno == 0)
-                        return -EIO;
-
-                return -errno;
-        }
+        if (r != 0)
+                return -errno ?: -EIO;
 
         m = new0(DissectedImage, 1);
         if (!m)
@@ -227,12 +220,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
 
         errno = 0;
         pl = blkid_probe_get_partitions(b);
-        if (!pl) {
-                if (errno == 0)
-                        return -ENOMEM;
-
-                return -errno;
-        }
+        if (!pl)
+                return -errno ?: -ENOMEM;
 
         udev = udev_new();
         if (!udev)
@@ -312,7 +301,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 _cleanup_udev_device_unref_ struct udev_device *q;
                 unsigned long long pflags;
                 blkid_partition pp;
-                const char *node;
+                const char *node, *sysname;
                 dev_t qn;
                 int nr;
 
@@ -327,6 +316,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                 if (st.st_rdev == qn)
                         continue;
 
+                /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see
+                 * https://github.com/systemd/systemd/issues/5806 */
+                sysname = udev_device_get_sysname(q);
+                if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb"))
+                        continue;
+
                 node = udev_device_get_devnode(q);
                 if (!node)
                         continue;
@@ -347,9 +342,6 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                         sd_id128_t type_id, id;
                         bool rw = true;
 
-                        if (pflags & GPT_FLAG_NO_AUTO)
-                                continue;
-
                         sid = blkid_partition_get_uuid(pp);
                         if (!sid)
                                 continue;
@@ -363,18 +355,37 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                 continue;
 
                         if (sd_id128_equal(type_id, GPT_HOME)) {
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 designator = PARTITION_HOME;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_SRV)) {
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 designator = PARTITION_SRV;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_ESP)) {
+
+                                /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined
+                                 * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the
+                                 * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */
+
+                                if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
+                                        continue;
+
                                 designator = PARTITION_ESP;
                                 fstype = "vfat";
                         }
 #ifdef GPT_ROOT_NATIVE
                         else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
 
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 /* If a root ID is specified, ignore everything but the root id */
                                 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
                                         continue;
@@ -384,6 +395,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
 
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 m->can_verity = true;
 
                                 /* Ignore verity unless a root hash is specified */
@@ -399,6 +413,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
 #ifdef GPT_ROOT_SECONDARY
                         else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
 
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 /* If a root ID is specified, ignore everything but the root id */
                                 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
                                         continue;
@@ -407,6 +424,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                                 architecture = SECONDARY_ARCHITECTURE;
                                 rw = !(pflags & GPT_FLAG_READ_ONLY);
                         } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 m->can_verity = true;
 
                                 /* Ignore verity unless root has is specified */
@@ -420,10 +441,17 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
                         }
 #endif
                         else if (sd_id128_equal(type_id, GPT_SWAP)) {
+
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 designator = PARTITION_SWAP;
                                 fstype = "swap";
                         } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
 
+                                if (pflags & GPT_FLAG_NO_AUTO)
+                                        continue;
+
                                 if (generic_node)
                                         multiple_generic = true;
                                 else {
@@ -563,6 +591,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI
 
                 if (streq_ptr(p->fstype, "crypto_LUKS"))
                         m->encrypted = true;
+
+                if (p->fstype && fstype_is_ro(p->fstype))
+                        p->rw = false;
         }
 
         *ret = m;
@@ -624,7 +655,9 @@ static int mount_partition(
                 DissectImageFlags flags) {
 
         const char *p, *options = NULL, *node, *fstype;
+        _cleanup_free_ char *chased = NULL;
         bool rw;
+        int r;
 
         assert(m);
         assert(where);
@@ -641,13 +674,17 @@ static int mount_partition(
 
         rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
 
-        if (directory)
-                p = strjoina(where, directory);
-        else
+        if (directory) {
+                r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased);
+                if (r < 0)
+                        return r;
+
+                p = chased;
+        } else
                 p = where;
 
         /* If requested, turn on discard support. */
-        if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs") &&
+        if (fstype_can_discard(fstype) &&
             ((flags & DISSECT_IMAGE_DISCARD) ||
              ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
                 options = "discard";
@@ -677,29 +714,30 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag
                 return r;
 
         if (m->partitions[PARTITION_ESP].found) {
-                const char *mp, *x;
+                const char *mp;
 
                 /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
 
-                mp = "/efi";
-                x = strjoina(where, mp);
-                r = dir_is_empty(x);
-                if (r == -ENOENT) {
-                        mp = "/boot";
-                        x = strjoina(where, mp);
-                        r = dir_is_empty(x);
-                }
-                if (r > 0) {
-                        r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+                FOREACH_STRING(mp, "/efi", "/boot") {
+                        _cleanup_free_ char *p = NULL;
+
+                        r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p);
                         if (r < 0)
-                                return r;
+                                continue;
+
+                        r = dir_is_empty(p);
+                        if (r > 0) {
+                                r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
+                                if (r < 0)
+                                        return r;
+                        }
                 }
         }
 
         return 0;
 }
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
 typedef struct DecryptedPartition {
         struct crypt_device *device;
         char *name;
@@ -714,7 +752,7 @@ struct DecryptedImage {
 #endif
 
 DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
         size_t i;
         int r;
 
@@ -740,7 +778,7 @@ DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
         return NULL;
 }
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
 
 static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
         _cleanup_free_ char *name = NULL, *node = NULL;
@@ -803,15 +841,19 @@ static int decrypt_partition(
 
         r = crypt_init(&cd, m->node);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
 
         r = crypt_load(cd, CRYPT_LUKS1, NULL);
-        if (r < 0)
+        if (r < 0) {
+                log_debug_errno(r, "Failed to load LUKS metadata: %m");
                 goto fail;
+        }
 
         r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
                                          ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
                                          ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
+        if (r < 0)
+                log_debug_errno(r, "Failed to activate LUKS device: %m");
         if (r == -EPERM) {
                 r = -EKEYREJECTED;
                 goto fail;
@@ -910,7 +952,7 @@ int dissected_image_decrypt(
                 DecryptedImage **ret) {
 
         _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
         unsigned i;
         int r;
 #endif
@@ -922,7 +964,7 @@ int dissected_image_decrypt(
          *
          *      = 0           → There was nothing to decrypt
          *      > 0           → Decrypted successfully
-         *      -ENOKEY       → There's some to decrypt but no key was supplied
+         *      -ENOKEY       → There's something to decrypt but no key was supplied
          *      -EKEYREJECTED → Passed key was not correct
          */
 
@@ -934,7 +976,7 @@ int dissected_image_decrypt(
                 return 0;
         }
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
         if (m->encrypted && !passphrase)
                 return -ENOKEY;
 
@@ -1016,7 +1058,7 @@ int dissected_image_decrypt_interactively(
         }
 }
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
 static int deferred_remove(DecryptedPartition *p) {
 
         struct dm_ioctl dm = {
@@ -1050,7 +1092,7 @@ static int deferred_remove(DecryptedPartition *p) {
 
 int decrypted_image_relinquish(DecryptedImage *d) {
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
         size_t i;
         int r;
 #endif
@@ -1060,7 +1102,7 @@ int decrypted_image_relinquish(DecryptedImage *d) {
         /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
          * that we don't clean it up ourselves either anymore */
 
-#ifdef HAVE_LIBCRYPTSETUP
+#if HAVE_LIBCRYPTSETUP
         for (i = 0; i < d->n_decrypted; i++) {
                 DecryptedPartition *p = d->decrypted + i;
 
@@ -1078,6 +1120,62 @@ int decrypted_image_relinquish(DecryptedImage *d) {
         return 0;
 }
 
+int root_hash_load(const char *image, void **ret, size_t *ret_size) {
+        _cleanup_free_ char *text = NULL;
+        _cleanup_free_ void *k = NULL;
+        size_t l;
+        int r;
+
+        assert(image);
+        assert(ret);
+        assert(ret_size);
+
+        if (is_device_path(image)) {
+                /* If we are asked to load the root hash for a device node, exit early */
+                *ret = NULL;
+                *ret_size = 0;
+                return 0;
+        }
+
+        r = getxattr_malloc(image, "user.verity.roothash", &text, true);
+        if (r < 0) {
+                char *fn, *e, *n;
+
+                if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
+                        return r;
+
+                fn = newa(char, strlen(image) + strlen(".roothash") + 1);
+                n = stpcpy(fn, image);
+                e = endswith(fn, ".raw");
+                if (e)
+                        n = e;
+
+                strcpy(n, ".roothash");
+
+                r = read_one_line_file(fn, &text);
+                if (r == -ENOENT) {
+                        *ret = NULL;
+                        *ret_size = 0;
+                        return 0;
+                }
+                if (r < 0)
+                        return r;
+        }
+
+        r = unhexmem(text, strlen(text), &k, &l);
+        if (r < 0)
+                return r;
+        if (l < sizeof(sd_id128_t))
+                return -EINVAL;
+
+        *ret = k;
+        *ret_size = l;
+
+        k = NULL;
+
+        return 1;
+}
+
 static const char *const partition_designator_table[] = {
         [PARTITION_ROOT] = "root",
         [PARTITION_ROOT_SECONDARY] = "root-secondary",