]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/veritysetup/veritysetup-generator.c
tree-wide: port everything over to new sd-id128 compund literal bliss
[thirdparty/systemd.git] / src / veritysetup / veritysetup-generator.c
index f2b74f3dc14a55541864091feaf0bb37cb9c86d8..1c68d283646412be33cb70cc654bb828f337fb18 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <errno.h>
 #include <stdbool.h>
 
 static const char *arg_dest = NULL;
 static bool arg_enabled = true;
+static bool arg_read_veritytab = true;
+static const char *arg_veritytab = NULL;
 static char *arg_root_hash = NULL;
 static char *arg_data_what = NULL;
 static char *arg_hash_what = NULL;
+static char *arg_options = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_data_what, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_hash_what, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_options, freep);
 
 static int create_device(void) {
-        _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL;
+        _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL,
+                            *root_hash_escaped = NULL, *options_escaped = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *to;
         int r;
@@ -57,7 +62,8 @@ static int create_device(void) {
 
         log_debug("Using root verity data device %s,\n"
                   "                  hash device %s,\n"
-                  "                and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash);
+                  "                      options %s,\n"
+                  "                and root hash %s.", arg_data_what, arg_hash_what, arg_options, arg_root_hash);
 
         u = fstab_node_to_udev_node(arg_data_what);
         if (!u)
@@ -80,6 +86,10 @@ static int create_device(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to generate unit name: %m");
 
+        options_escaped = specifier_escape(strempty(arg_options));
+        if (!options_escaped)
+                return log_oom();
+
         root_hash_escaped = specifier_escape(arg_root_hash);
         if (!root_hash_escaped)
                 return log_oom();
@@ -97,22 +107,22 @@ static int create_device(void) {
                 "Conflicts=umount.target\n"
                 "BindsTo=%s %s\n"
                 "IgnoreOnIsolate=true\n"
-                "After=cryptsetup-pre.target %s %s\n"
-                "Before=cryptsetup.target umount.target\n"
+                "After=veritysetup-pre.target systemd-udevd-kernel.socket %s %s\n"
+                "Before=veritysetup.target umount.target\n"
                 "\n[Service]\n"
                 "Type=oneshot\n"
                 "RemainAfterExit=yes\n"
-                "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n"
+                "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s' '%s'\n"
                 "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n",
                 d, e,
                 d, e,
-                u_escaped, v_escaped, root_hash_escaped);
+                u_escaped, v_escaped, root_hash_escaped, options_escaped);
 
         r = fflush_and_check(f);
         if (r < 0)
                 return log_error_errno(r, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE": %m");
 
-        to = strjoina(arg_dest, "/cryptsetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE);
+        to = strjoina(arg_dest, "/veritysetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE);
 
         (void) mkdir_parents(to, 0755);
         if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE, to) < 0)
@@ -132,6 +142,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 else
                         arg_enabled = r;
 
+        } else if (streq(key, "veritytab")) {
+
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value);
+                else
+                        arg_read_veritytab = r;
+
         } else if (proc_cmdline_key_streq(key, "roothash")) {
 
                 if (proc_cmdline_value_missing(key, value))
@@ -158,6 +176,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 r = free_and_strdup(&arg_hash_what, value);
                 if (r < 0)
                         return log_oom();
+
+        } else if (proc_cmdline_key_streq(key, "systemd.verity_root_options")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = free_and_strdup(&arg_options, value);
+                if (r < 0)
+                        return log_oom();
+
         }
 
         return 0;
@@ -166,7 +194,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 static int determine_devices(void) {
         _cleanup_free_ void *m = NULL;
         sd_id128_t root_uuid, verity_uuid;
-        char ids[37];
         size_t l;
         int r;
 
@@ -189,7 +216,7 @@ static int determine_devices(void) {
         if (!arg_data_what) {
                 memcpy(&root_uuid, m, sizeof(root_uuid));
 
-                arg_data_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid, ids));
+                arg_data_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(root_uuid));
                 if (!arg_data_what)
                         return log_oom();
         }
@@ -197,7 +224,7 @@ static int determine_devices(void) {
         if (!arg_hash_what) {
                 memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
 
-                arg_hash_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid, ids));
+                arg_hash_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(verity_uuid));
                 if (!arg_hash_what)
                         return log_oom();
         }
@@ -205,21 +232,216 @@ static int determine_devices(void) {
         return 1;
 }
 
+static int create_disk(
+                const char *name,
+                const char *data_device,
+                const char *hash_device,
+                const char *roothash,
+                const char *options,
+                const char *source) {
+
+        _cleanup_free_ char *n = NULL, *dd = NULL, *du = NULL, *hd = NULL, *hu = NULL, *e = NULL,
+                            *du_escaped = NULL, *hu_escaped = NULL, *name_escaped = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *dmname;
+        bool noauto, nofail, netdev, attach_in_initrd;
+        int r;
+
+        assert(name);
+        assert(data_device);
+        assert(hash_device);
+        assert(roothash);
+
+        noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
+        nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
+        netdev = fstab_test_option(options, "_netdev\0");
+        attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0");
+
+        name_escaped = specifier_escape(name);
+        if (!name_escaped)
+                return log_oom();
+
+        e = unit_name_escape(name);
+        if (!e)
+                return log_oom();
+
+        du = fstab_node_to_udev_node(data_device);
+        if (!du)
+                return log_oom();
+
+        hu = fstab_node_to_udev_node(hash_device);
+        if (!hu)
+                return log_oom();
+
+        r = unit_name_build("systemd-veritysetup", e, ".service", &n);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate unit name: %m");
+
+        du_escaped = specifier_escape(du);
+        if (!du_escaped)
+                return log_oom();
+
+        hu_escaped = specifier_escape(hu);
+        if (!hu_escaped)
+                return log_oom();
+
+        r = unit_name_from_path(du, ".device", &dd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate unit name: %m");
+
+        r = unit_name_from_path(hu, ".device", &hd);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate unit name: %m");
+
+        r = generator_open_unit_file(arg_dest, NULL, n, &f);
+        if (r < 0)
+                return r;
+
+        r = generator_write_veritysetup_unit_section(f, source);
+        if (r < 0)
+                return r;
+
+        if (netdev)
+                fprintf(f, "After=remote-fs-pre.target\n");
+
+        /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
+        if (!attach_in_initrd)
+                fprintf(f, "Conflicts=umount.target\n");
+
+        if (!nofail)
+                fprintf(f,
+                        "Before=%s\n",
+                        netdev ? "remote-veritysetup.target" : "veritysetup.target");
+
+        if (path_startswith(du, "/dev/"))
+                fprintf(f,
+                        "BindsTo=%s\n"
+                        "After=%s\n"
+                        "Before=umount.target\n",
+                        dd, dd);
+        else
+                /* For loopback devices, add systemd-tmpfiles-setup-dev.service
+                   dependency to ensure that loopback support is available in
+                   the kernel (/dev/loop-control needs to exist) */
+                fprintf(f,
+                        "RequiresMountsFor=%s\n"
+                        "Requires=systemd-tmpfiles-setup-dev.service\n"
+                        "After=systemd-tmpfiles-setup-dev.service\n",
+                        du_escaped);
+
+        if (path_startswith(hu, "/dev/"))
+                fprintf(f,
+                        "BindsTo=%s\n"
+                        "After=%s\n"
+                        "Before=umount.target\n",
+                        hd, hd);
+        else
+                /* For loopback devices, add systemd-tmpfiles-setup-dev.service
+                   dependency to ensure that loopback support is available in
+                   the kernel (/dev/loop-control needs to exist) */
+                fprintf(f,
+                        "RequiresMountsFor=%s\n"
+                        "Requires=systemd-tmpfiles-setup-dev.service\n"
+                        "After=systemd-tmpfiles-setup-dev.service\n",
+                        hu_escaped);
+
+        r = generator_write_veritysetup_service_section(f, name, du_escaped, hu_escaped, roothash, options);
+        if (r < 0)
+                return r;
+
+        r = fflush_and_check(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write unit file %s: %m", n);
+
+        if (!noauto) {
+                r = generator_add_symlink(arg_dest,
+                                          netdev ? "remote-veritysetup.target" : "veritysetup.target",
+                                          nofail ? "wants" : "requires", n);
+                if (r < 0)
+                        return r;
+        }
+
+        dmname = strjoina("dev-mapper-", e, ".device");
+        return generator_add_symlink(arg_dest, dmname, "requires", n);
+}
+
+static int add_veritytab_devices(void) {
+        _cleanup_fclose_ FILE *f = NULL;
+        unsigned veritytab_line = 0;
+        int r;
+
+        if (!arg_read_veritytab)
+                return 0;
+
+        r = fopen_unlocked(arg_veritytab, "re", &f);
+        if (r < 0) {
+                if (errno != ENOENT)
+                        log_error_errno(errno, "Failed to open %s: %m", arg_veritytab);
+                return 0;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL,
+                                    *roothash = NULL, *options = NULL;
+                char *l, *data_uuid, *hash_uuid;
+
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read %s: %m", arg_veritytab);
+                if (r == 0)
+                        break;
+
+                veritytab_line++;
+
+                l = strstrip(line);
+                if (IN_SET(l[0], 0, '#'))
+                        continue;
+
+                r = sscanf(l, "%ms %ms %ms %ms %ms", &name, &data_device, &hash_device, &roothash, &options);
+                if (!IN_SET(r, 4, 5)) {
+                        log_error("Failed to parse %s:%u, ignoring.", arg_veritytab, veritytab_line);
+                        continue;
+                }
+
+                data_uuid = startswith(data_device, "UUID=");
+                if (!data_uuid)
+                        data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/");
+
+                hash_uuid = startswith(hash_device, "UUID=");
+                if (!hash_uuid)
+                        hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/");
+
+                r = create_disk(name,
+                                data_device,
+                                hash_device,
+                                roothash,
+                                options,
+                                arg_veritytab);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
         int r;
 
         assert_se(arg_dest = dest);
 
+        arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
+
         r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
         if (r < 0)
                 return log_warning_errno(r, "Failed to parse kernel command line: %m");
 
-        /* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab
-         * or similar to define additional mappings */
-
         if (!arg_enabled)
                 return 0;
 
+        r = add_veritytab_devices();
+        if (r < 0)
+                return r;
+
         r = determine_devices();
         if (r < 0)
                 return r;