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 "systemd-veritysetup@root.service"
27 static const char *arg_dest
= NULL
;
28 static bool arg_enabled
= true;
29 static bool arg_read_veritytab
= true;
30 static const char *arg_veritytab
= NULL
;
31 static char *arg_root_hash
= NULL
;
32 static char *arg_data_what
= NULL
;
33 static char *arg_hash_what
= NULL
;
34 static char *arg_options
= NULL
;
36 STATIC_DESTRUCTOR_REGISTER(arg_root_hash
, freep
);
37 STATIC_DESTRUCTOR_REGISTER(arg_data_what
, freep
);
38 STATIC_DESTRUCTOR_REGISTER(arg_hash_what
, freep
);
39 STATIC_DESTRUCTOR_REGISTER(arg_options
, freep
);
41 static int create_device(void) {
42 _cleanup_free_
char *u
= NULL
, *v
= NULL
, *d
= NULL
, *e
= NULL
, *u_escaped
= NULL
, *v_escaped
= NULL
,
43 *root_hash_escaped
= NULL
, *options_escaped
= NULL
;
44 _cleanup_fclose_
FILE *f
= NULL
;
48 /* If all three pieces of information are missing, then verity is turned off */
49 if (!arg_root_hash
&& !arg_data_what
&& !arg_hash_what
)
52 /* if one of them is missing however, the data is simply incomplete and this is an error */
54 log_error("Verity information incomplete, root hash unspecified.");
56 log_error("Verity information incomplete, root data device unspecified.");
58 log_error("Verity information incomplete, root hash device unspecified.");
60 if (!arg_root_hash
|| !arg_data_what
|| !arg_hash_what
)
63 log_debug("Using root verity data device %s,\n"
66 " and root hash %s.", arg_data_what
, arg_hash_what
, arg_options
, arg_root_hash
);
68 u
= fstab_node_to_udev_node(arg_data_what
);
71 v
= fstab_node_to_udev_node(arg_hash_what
);
75 u_escaped
= specifier_escape(u
);
78 v_escaped
= specifier_escape(v
);
82 r
= unit_name_from_path(u
, ".device", &d
);
84 return log_error_errno(r
, "Failed to generate unit name: %m");
85 r
= unit_name_from_path(v
, ".device", &e
);
87 return log_error_errno(r
, "Failed to generate unit name: %m");
89 options_escaped
= specifier_escape(strempty(arg_options
));
93 root_hash_escaped
= specifier_escape(arg_root_hash
);
94 if (!root_hash_escaped
)
97 r
= generator_open_unit_file(arg_dest
, NULL
, SYSTEMD_VERITYSETUP_SERVICE
, &f
);
103 "Description=Integrity Protection Setup for %%I\n"
104 "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"
105 "SourcePath=/proc/cmdline\n"
106 "DefaultDependencies=no\n"
107 "Conflicts=umount.target\n"
109 "IgnoreOnIsolate=true\n"
110 "After=veritysetup-pre.target systemd-udevd-kernel.socket %s %s\n"
111 "Before=veritysetup.target umount.target\n"
114 "RemainAfterExit=yes\n"
115 "ExecStart=" ROOTLIBEXECDIR
"/systemd-veritysetup attach root '%s' '%s' '%s' '%s'\n"
116 "ExecStop=" ROOTLIBEXECDIR
"/systemd-veritysetup detach root\n",
119 u_escaped
, v_escaped
, root_hash_escaped
, options_escaped
);
121 r
= fflush_and_check(f
);
123 return log_error_errno(r
, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE
": %m");
125 to
= strjoina(arg_dest
, "/veritysetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE
);
127 (void) mkdir_parents(to
, 0755);
128 if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE
, to
) < 0)
129 return log_error_errno(errno
, "Failed to create symlink %s: %m", to
);
134 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
137 if (proc_cmdline_key_streq(key
, "systemd.verity")) {
139 r
= value
? parse_boolean(value
) : 1;
141 log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value
);
145 } else if (streq(key
, "veritytab")) {
147 r
= value
? parse_boolean(value
) : 1;
149 log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value
);
151 arg_read_veritytab
= r
;
153 } else if (proc_cmdline_key_streq(key
, "roothash")) {
155 if (proc_cmdline_value_missing(key
, value
))
158 r
= free_and_strdup(&arg_root_hash
, value
);
162 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_data")) {
164 if (proc_cmdline_value_missing(key
, value
))
167 r
= free_and_strdup(&arg_data_what
, value
);
171 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_hash")) {
173 if (proc_cmdline_value_missing(key
, value
))
176 r
= free_and_strdup(&arg_hash_what
, value
);
180 } else if (proc_cmdline_key_streq(key
, "systemd.verity_root_options")) {
182 if (proc_cmdline_value_missing(key
, value
))
185 r
= free_and_strdup(&arg_options
, value
);
194 static int determine_devices(void) {
195 _cleanup_free_
void *m
= NULL
;
196 sd_id128_t root_uuid
, verity_uuid
;
197 char ids
[ID128_UUID_STRING_MAX
];
201 /* Try to automatically derive the root data and hash device paths from the root hash */
206 if (arg_data_what
&& arg_hash_what
)
209 r
= unhexmem(arg_root_hash
, strlen(arg_root_hash
), &m
, &l
);
211 return log_error_errno(r
, "Failed to parse root hash: %s", arg_root_hash
);
212 if (l
< sizeof(sd_id128_t
)) {
213 log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition.");
217 if (!arg_data_what
) {
218 memcpy(&root_uuid
, m
, sizeof(root_uuid
));
220 arg_data_what
= path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid
, ids
));
225 if (!arg_hash_what
) {
226 memcpy(&verity_uuid
, (uint8_t*) m
+ l
- sizeof(verity_uuid
), sizeof(verity_uuid
));
228 arg_hash_what
= path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid
, ids
));
236 static int create_disk(
238 const char *data_device
,
239 const char *hash_device
,
240 const char *roothash
,
242 const char *source
) {
244 _cleanup_free_
char *n
= NULL
, *dd
= NULL
, *du
= NULL
, *hd
= NULL
, *hu
= NULL
, *e
= NULL
,
245 *du_escaped
= NULL
, *hu_escaped
= NULL
, *name_escaped
= NULL
;
246 _cleanup_fclose_
FILE *f
= NULL
;
248 bool noauto
, nofail
, netdev
, attach_in_initrd
;
256 noauto
= fstab_test_yes_no_option(options
, "noauto\0" "auto\0");
257 nofail
= fstab_test_yes_no_option(options
, "nofail\0" "fail\0");
258 netdev
= fstab_test_option(options
, "_netdev\0");
259 attach_in_initrd
= fstab_test_option(options
, "x-initrd.attach\0");
261 name_escaped
= specifier_escape(name
);
265 e
= unit_name_escape(name
);
269 du
= fstab_node_to_udev_node(data_device
);
273 hu
= fstab_node_to_udev_node(hash_device
);
277 r
= unit_name_build("systemd-veritysetup", e
, ".service", &n
);
279 return log_error_errno(r
, "Failed to generate unit name: %m");
281 du_escaped
= specifier_escape(du
);
285 hu_escaped
= specifier_escape(hu
);
289 r
= unit_name_from_path(du
, ".device", &dd
);
291 return log_error_errno(r
, "Failed to generate unit name: %m");
293 r
= unit_name_from_path(hu
, ".device", &hd
);
295 return log_error_errno(r
, "Failed to generate unit name: %m");
297 r
= generator_open_unit_file(arg_dest
, NULL
, n
, &f
);
301 r
= generator_write_veritysetup_unit_section(f
, source
);
306 fprintf(f
, "After=remote-fs-pre.target\n");
308 /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
309 if (!attach_in_initrd
)
310 fprintf(f
, "Conflicts=umount.target\n");
315 netdev
? "remote-veritysetup.target" : "veritysetup.target");
317 if (path_startswith(du
, "/dev/"))
321 "Before=umount.target\n",
324 /* For loopback devices, add systemd-tmpfiles-setup-dev.service
325 dependency to ensure that loopback support is available in
326 the kernel (/dev/loop-control needs to exist) */
328 "RequiresMountsFor=%s\n"
329 "Requires=systemd-tmpfiles-setup-dev.service\n"
330 "After=systemd-tmpfiles-setup-dev.service\n",
333 if (path_startswith(hu
, "/dev/"))
337 "Before=umount.target\n",
340 /* For loopback devices, add systemd-tmpfiles-setup-dev.service
341 dependency to ensure that loopback support is available in
342 the kernel (/dev/loop-control needs to exist) */
344 "RequiresMountsFor=%s\n"
345 "Requires=systemd-tmpfiles-setup-dev.service\n"
346 "After=systemd-tmpfiles-setup-dev.service\n",
349 r
= generator_write_veritysetup_service_section(f
, name
, du_escaped
, hu_escaped
, roothash
, options
);
353 r
= fflush_and_check(f
);
355 return log_error_errno(r
, "Failed to write unit file %s: %m", n
);
358 r
= generator_add_symlink(arg_dest
,
359 netdev
? "remote-veritysetup.target" : "veritysetup.target",
360 nofail
? "wants" : "requires", n
);
365 dmname
= strjoina("dev-mapper-", e
, ".device");
366 return generator_add_symlink(arg_dest
, dmname
, "requires", n
);
369 static int add_veritytab_devices(void) {
370 _cleanup_fclose_
FILE *f
= NULL
;
371 unsigned veritytab_line
= 0;
374 if (!arg_read_veritytab
)
377 r
= fopen_unlocked(arg_veritytab
, "re", &f
);
380 log_error_errno(errno
, "Failed to open %s: %m", arg_veritytab
);
385 _cleanup_free_
char *line
= NULL
, *name
= NULL
, *data_device
= NULL
, *hash_device
= NULL
,
386 *roothash
= NULL
, *options
= NULL
;
387 char *l
, *data_uuid
, *hash_uuid
;
389 r
= read_line(f
, LONG_LINE_MAX
, &line
);
391 return log_error_errno(r
, "Failed to read %s: %m", arg_veritytab
);
398 if (IN_SET(l
[0], 0, '#'))
401 r
= sscanf(l
, "%ms %ms %ms %ms %ms", &name
, &data_device
, &hash_device
, &roothash
, &options
);
402 if (!IN_SET(r
, 4, 5)) {
403 log_error("Failed to parse %s:%u, ignoring.", arg_veritytab
, veritytab_line
);
407 data_uuid
= startswith(data_device
, "UUID=");
409 data_uuid
= path_startswith(data_device
, "/dev/disk/by-uuid/");
411 hash_uuid
= startswith(hash_device
, "UUID=");
413 hash_uuid
= path_startswith(hash_device
, "/dev/disk/by-uuid/");
415 r
= create_disk(name
,
428 static int run(const char *dest
, const char *dest_early
, const char *dest_late
) {
431 assert_se(arg_dest
= dest
);
433 arg_veritytab
= getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
435 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
437 return log_warning_errno(r
, "Failed to parse kernel command line: %m");
442 r
= add_veritytab_devices();
446 r
= determine_devices();
450 return create_device();
453 DEFINE_MAIN_GENERATOR_FUNCTION(run
);