1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
12 #include "fstab-util.h"
13 #include "generator.h"
14 #include "hexdecoct.h"
15 #include "id128-util.h"
16 #include "main-func.h"
18 #include "parse-util.h"
19 #include "path-util.h"
20 #include "proc-cmdline.h"
21 #include "specifier.h"
22 #include "string-util.h"
23 #include "unit-name.h"
25 #define SYSTEMD_VERITYSETUP_SERVICE_ROOT "systemd-veritysetup@root.service"
26 #define SYSTEMD_VERITYSETUP_SERVICE_USR "systemd-veritysetup@usr.service"
28 static const char *arg_dest
= NULL
;
29 static bool arg_enabled
= true;
30 static bool arg_read_veritytab
= true;
31 static const char *arg_veritytab
= NULL
;
32 static char *arg_root_hash
= NULL
;
33 static char *arg_root_data_what
= NULL
;
34 static char *arg_root_hash_what
= NULL
;
35 static char *arg_root_options
= NULL
;
36 static char *arg_usr_hash
= NULL
;
37 static char *arg_usr_data_what
= NULL
;
38 static char *arg_usr_hash_what
= NULL
;
39 static char *arg_usr_options
= NULL
;
41 STATIC_DESTRUCTOR_REGISTER(arg_root_hash
, freep
);
42 STATIC_DESTRUCTOR_REGISTER(arg_root_data_what
, freep
);
43 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_what
, freep
);
44 STATIC_DESTRUCTOR_REGISTER(arg_root_options
, freep
);
45 STATIC_DESTRUCTOR_REGISTER(arg_usr_hash
, freep
);
46 STATIC_DESTRUCTOR_REGISTER(arg_usr_data_what
, freep
);
47 STATIC_DESTRUCTOR_REGISTER(arg_usr_hash_what
, freep
);
48 STATIC_DESTRUCTOR_REGISTER(arg_usr_options
, freep
);
50 static int create_special_device(
54 const char *data_what
,
55 const char *hash_what
,
56 const char *options
) {
58 _cleanup_free_
char *u
= NULL
, *v
= NULL
, *d
= NULL
, *e
= NULL
;
59 _cleanup_fclose_
FILE *f
= NULL
;
62 /* Creates a systemd-veritysetup@.service instance for the special kernel cmdline specified root + usr devices. */
67 /* If all three pieces of information are missing, then verity is turned off */
68 if (!roothash
&& !data_what
&& !hash_what
)
71 /* if one of them is missing however, the data is simply incomplete and this is an error */
73 log_error("Verity information for %s incomplete, root hash unspecified.", name
);
75 log_error("Verity information for %s incomplete, data device unspecified.", name
);
77 log_error("Verity information for %s incomplete, hash device unspecified.", name
);
79 if (!roothash
|| !data_what
|| !hash_what
)
82 log_debug("Using %s verity data device %s, hash device %s, options %s, and hash %s.", name
, data_what
, hash_what
, options
, roothash
);
84 u
= fstab_node_to_udev_node(data_what
);
87 v
= fstab_node_to_udev_node(hash_what
);
91 r
= unit_name_from_path(u
, ".device", &d
);
93 return log_error_errno(r
, "Failed to generate unit name: %m");
94 r
= unit_name_from_path(v
, ".device", &e
);
96 return log_error_errno(r
, "Failed to generate unit name: %m");
98 r
= generator_open_unit_file(arg_dest
, NULL
, service
, &f
);
102 r
= generator_write_veritysetup_unit_section(f
, "/proc/cmdline");
107 "Before=veritysetup.target\n"
113 r
= generator_write_veritysetup_service_section(f
, name
, u
, v
, roothash
, options
);
117 r
= fflush_and_check(f
);
119 return log_error_errno(r
, "Failed to write file unit %s: %m", service
);
121 r
= generator_add_symlink(arg_dest
, "veritysetup.target", "requires", service
);
128 static int create_root_device(void) {
129 return create_special_device("root", SYSTEMD_VERITYSETUP_SERVICE_ROOT
, arg_root_hash
, arg_root_data_what
, arg_root_hash_what
, arg_root_options
);
132 static int create_usr_device(void) {
133 return create_special_device("usr", SYSTEMD_VERITYSETUP_SERVICE_USR
, arg_usr_hash
, arg_usr_data_what
, arg_usr_hash_what
, arg_usr_options
);
136 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
141 if (streq(key
, "systemd.verity")) {
143 r
= value
? parse_boolean(value
) : 1;
145 log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value
);
149 } else if (streq(key
, "veritytab")) {
151 r
= value
? parse_boolean(value
) : 1;
153 log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value
);
155 arg_read_veritytab
= r
;
157 } else if (streq(key
, "roothash")) {
159 if (proc_cmdline_value_missing(key
, value
))
162 r
= free_and_strdup(&arg_root_hash
, value
);
166 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_data")) {
168 if (proc_cmdline_value_missing(key
, value
))
171 r
= free_and_strdup(&arg_root_data_what
, value
);
175 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_hash")) {
177 if (proc_cmdline_value_missing(key
, value
))
180 r
= free_and_strdup(&arg_root_hash_what
, value
);
184 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_options")) {
186 if (proc_cmdline_value_missing(key
, value
))
189 r
= free_and_strdup(&arg_root_options
, value
);
193 } else if (streq(key
, "usrhash")) {
195 if (proc_cmdline_value_missing(key
, value
))
198 r
= free_and_strdup(&arg_usr_hash
, value
);
202 } else if (proc_cmdline_key_streq(key
, "systemd.verity_usr_data")) {
204 if (proc_cmdline_value_missing(key
, value
))
207 r
= free_and_strdup(&arg_usr_data_what
, value
);
211 } else if (proc_cmdline_key_streq(key
, "systemd.verity_usr_hash")) {
213 if (proc_cmdline_value_missing(key
, value
))
216 r
= free_and_strdup(&arg_usr_hash_what
, value
);
220 } else if (proc_cmdline_key_streq(key
, "systemd.verity_usr_options")) {
222 if (proc_cmdline_value_missing(key
, value
))
225 r
= free_and_strdup(&arg_usr_options
, value
);
234 static int determine_device(
240 sd_id128_t data_uuid
, verity_uuid
;
241 _cleanup_free_
void *m
= NULL
;
252 if (*data_what
&& *hash_what
)
255 r
= unhexmem(hash
, &m
, &l
);
257 return log_error_errno(r
, "Failed to parse hash: %s", hash
);
258 if (l
< sizeof(sd_id128_t
)) {
259 log_debug("Root hash for %s is shorter than 128 bits (32 characters), ignoring for discovering verity partition.", name
);
264 memcpy(&data_uuid
, m
, sizeof(data_uuid
));
266 *data_what
= path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid
));
272 memcpy(&verity_uuid
, (uint8_t*) m
+ l
- sizeof(verity_uuid
), sizeof(verity_uuid
));
274 *hash_what
= path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid
));
279 log_info("Using data device %s and hash device %s for %s.", *data_what
, *hash_what
, name
);
284 static int determine_devices(void) {
287 r
= determine_device("root", arg_root_hash
, &arg_root_data_what
, &arg_root_hash_what
);
291 return determine_device("usr", arg_usr_hash
, &arg_usr_data_what
, &arg_usr_hash_what
);
294 static bool attach_in_initrd(const char *name
, const char *options
) {
297 /* Imply x-initrd.attach in case the volume name is among those defined in the Discoverable Partition
298 * Specification for partitions that we require to be mounted during the initrd → host transition,
299 * i.e. for the root fs itself, and /usr/. This mirrors similar behaviour in
300 * systemd-fstab-generator. */
302 return fstab_test_option(options
, "x-initrd.attach\0") ||
303 STR_IN_SET(name
, "root", "usr");
306 static int create_veritytab_device(
308 const char *data_device
,
309 const char *hash_device
,
310 const char *roothash
,
312 const char *source
) {
314 _cleanup_free_
char *n
= NULL
, *dd
= NULL
, *du
= NULL
, *hd
= NULL
, *hu
= NULL
, *e
= NULL
,
315 *du_escaped
= NULL
, *hu_escaped
= NULL
, *name_escaped
= NULL
;
316 _cleanup_fclose_
FILE *f
= NULL
;
318 bool noauto
, nofail
, netdev
, need_loop
= false;
321 /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */
328 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
329 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
330 netdev
= fstab_test_option(options
, "_netdev\0");
332 name_escaped
= specifier_escape(name
);
336 e
= unit_name_escape(name
);
340 du
= fstab_node_to_udev_node(data_device
);
344 hu
= fstab_node_to_udev_node(hash_device
);
348 r
= unit_name_build("systemd-veritysetup", e
, ".service", &n
);
350 return log_error_errno(r
, "Failed to generate unit name: %m");
352 du_escaped
= specifier_escape(du
);
356 hu_escaped
= specifier_escape(hu
);
360 r
= unit_name_from_path(du
, ".device", &dd
);
362 return log_error_errno(r
, "Failed to generate unit name: %m");
364 r
= unit_name_from_path(hu
, ".device", &hd
);
366 return log_error_errno(r
, "Failed to generate unit name: %m");
368 r
= generator_open_unit_file(arg_dest
, NULL
, n
, &f
);
372 r
= generator_write_veritysetup_unit_section(f
, source
);
377 fprintf(f
, "After=remote-fs-pre.target\n");
379 /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
380 if (!attach_in_initrd(name
, options
))
382 "Conflicts=umount.target\n"
383 "Before=umount.target\n");
388 netdev
? "remote-veritysetup.target" : "veritysetup.target");
390 if (path_startswith(du
, "/dev/"))
396 fprintf(f
, "RequiresMountsFor=%s\n", du_escaped
);
400 if (path_startswith(hu
, "/dev/"))
406 fprintf(f
, "RequiresMountsFor=%s\n", hu_escaped
);
411 /* For loopback devices make sure to explicitly load loop.ko, as this code might run very
412 * early where device nodes created via systemd-tmpfiles-setup-dev.service might not be
413 * around yet. Hence let's sync on the module itself. */
415 "Wants=modprobe@loop.service\n"
416 "After=modprobe@loop.service\n");
418 r
= generator_write_veritysetup_service_section(f
, name
, du
, hu
, roothash
, options
);
422 r
= fflush_and_check(f
);
424 return log_error_errno(r
, "Failed to write unit file %s: %m", n
);
427 r
= generator_add_symlink(arg_dest
,
428 netdev
? "remote-veritysetup.target" : "veritysetup.target",
429 nofail
? "wants" : "requires", n
);
434 dmname
= strjoina("dev-mapper-", e
, ".device");
435 return generator_add_symlink(arg_dest
, dmname
, "requires", n
);
438 static int add_veritytab_devices(void) {
439 _cleanup_fclose_
FILE *f
= NULL
;
440 unsigned veritytab_line
= 0;
443 if (!arg_read_veritytab
)
446 r
= fopen_unlocked(arg_veritytab
, "re", &f
);
449 log_error_errno(errno
, "Failed to open %s: %m", arg_veritytab
);
454 _cleanup_free_
char *line
= NULL
, *name
= NULL
, *data_device
= NULL
, *hash_device
= NULL
,
455 *roothash
= NULL
, *options
= NULL
;
456 char *data_uuid
, *hash_uuid
;
458 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
460 return log_error_errno(r
, "Failed to read %s: %m", arg_veritytab
);
466 if (IN_SET(line
[0], 0, '#'))
469 r
= sscanf(line
, "%ms %ms %ms %ms %ms", &name
, &data_device
, &hash_device
, &roothash
, &options
);
470 if (!IN_SET(r
, 4, 5)) {
471 log_error("Failed to parse %s:%u, ignoring.", arg_veritytab
, veritytab_line
);
475 data_uuid
= startswith(data_device
, "UUID=");
477 data_uuid
= path_startswith(data_device
, "/dev/disk/by-uuid/");
479 hash_uuid
= startswith(hash_device
, "UUID=");
481 hash_uuid
= path_startswith(hash_device
, "/dev/disk/by-uuid/");
483 r
= create_veritytab_device(
497 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
500 assert_se(arg_dest
= dest
);
502 arg_veritytab
= getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
504 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
506 return log_warning_errno(r
, "Failed to parse kernel command line: %m");
511 r
= add_veritytab_devices();
515 r
= determine_devices();
519 r
= create_root_device();
523 return create_usr_device();
526 DEFINE_MAIN_GENERATOR_FUNCTION(run
);