]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/veritysetup/veritysetup-generator.c
veritysetup: minor renaming of functions
[thirdparty/systemd.git] / src / veritysetup / veritysetup-generator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2f3dfc6f 2
dccca82b 3#include <errno.h>
2f3dfc6f
LP
4#include <stdbool.h>
5#include <stdlib.h>
6#include <sys/stat.h>
7#include <unistd.h>
8
9#include "alloc-util.h"
10#include "fd-util.h"
11#include "fileio.h"
12#include "fstab-util.h"
fb883e75 13#include "generator.h"
2f3dfc6f
LP
14#include "hexdecoct.h"
15#include "id128-util.h"
d850ad9a 16#include "main-func.h"
2f3dfc6f
LP
17#include "mkdir.h"
18#include "parse-util.h"
2d9b74ba 19#include "path-util.h"
2f3dfc6f 20#include "proc-cmdline.h"
98bad05e 21#include "specifier.h"
2f3dfc6f
LP
22#include "string-util.h"
23#include "unit-name.h"
24
78b408d2
MB
25#define SYSTEMD_VERITYSETUP_SERVICE_ROOT "systemd-veritysetup@root.service"
26#define SYSTEMD_VERITYSETUP_SERVICE_USR "systemd-veritysetup@usr.service"
fb883e75 27
2cb52121 28static const char *arg_dest = NULL;
2f3dfc6f 29static bool arg_enabled = true;
08b04ec7
GP
30static bool arg_read_veritytab = true;
31static const char *arg_veritytab = NULL;
2f3dfc6f 32static char *arg_root_hash = NULL;
78b408d2
MB
33static char *arg_root_data_what = NULL;
34static char *arg_root_hash_what = NULL;
35static char *arg_root_options = NULL;
36static char *arg_usr_hash = NULL;
37static char *arg_usr_data_what = NULL;
38static char *arg_usr_hash_what = NULL;
39static char *arg_usr_options = NULL;
2f3dfc6f 40
d850ad9a 41STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
78b408d2
MB
42STATIC_DESTRUCTOR_REGISTER(arg_root_data_what, freep);
43STATIC_DESTRUCTOR_REGISTER(arg_root_hash_what, freep);
44STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
45STATIC_DESTRUCTOR_REGISTER(arg_usr_hash, freep);
46STATIC_DESTRUCTOR_REGISTER(arg_usr_data_what, freep);
47STATIC_DESTRUCTOR_REGISTER(arg_usr_hash_what, freep);
48STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
49
deb60ef9 50static int create_special_device(
78b408d2
MB
51 const char *name,
52 const char *service,
953006dc 53 const char *roothash,
78b408d2
MB
54 const char *data_what,
55 const char *hash_what,
56 const char *options) {
d850ad9a 57
953006dc 58 _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL;
2f3dfc6f 59 _cleanup_fclose_ FILE *f = NULL;
2f3dfc6f
LP
60 int r;
61
deb60ef9
LP
62 /* Creates a systemd-veritysetup@.service instance for the special kernel cmdline specified root + usr devices. */
63
78b408d2
MB
64 assert(name);
65 assert(service);
66
2f3dfc6f 67 /* If all three pieces of information are missing, then verity is turned off */
953006dc 68 if (!roothash && !data_what && !hash_what)
2f3dfc6f
LP
69 return 0;
70
71 /* if one of them is missing however, the data is simply incomplete and this is an error */
953006dc
LP
72 if (!roothash)
73 log_error("Verity information for %s incomplete, root hash unspecified.", name);
78b408d2
MB
74 if (!data_what)
75 log_error("Verity information for %s incomplete, data device unspecified.", name);
76 if (!hash_what)
77 log_error("Verity information for %s incomplete, hash device unspecified.", name);
78
953006dc 79 if (!roothash || !data_what || !hash_what)
2f3dfc6f
LP
80 return -EINVAL;
81
953006dc 82 log_debug("Using %s verity data device %s, hash device %s, options %s, and hash %s.", name, data_what, hash_what, options, roothash);
2f3dfc6f 83
78b408d2 84 u = fstab_node_to_udev_node(data_what);
2f3dfc6f
LP
85 if (!u)
86 return log_oom();
78b408d2 87 v = fstab_node_to_udev_node(hash_what);
2f3dfc6f
LP
88 if (!v)
89 return log_oom();
90
91 r = unit_name_from_path(u, ".device", &d);
92 if (r < 0)
1a012455 93 return log_error_errno(r, "Failed to generate unit name: %m");
2f3dfc6f
LP
94 r = unit_name_from_path(v, ".device", &e);
95 if (r < 0)
1a012455 96 return log_error_errno(r, "Failed to generate unit name: %m");
2f3dfc6f 97
78b408d2 98 r = generator_open_unit_file(arg_dest, NULL, service, &f);
fb883e75
ZJS
99 if (r < 0)
100 return r;
2f3dfc6f 101
953006dc
LP
102 r = generator_write_veritysetup_unit_section(f, "/proc/cmdline");
103 if (r < 0)
104 return r;
105
2f3dfc6f 106 fprintf(f,
bf1484c7 107 "Before=veritysetup.target\n"
2f3dfc6f 108 "BindsTo=%s %s\n"
bf1484c7 109 "After=%s %s\n",
2f3dfc6f 110 d, e,
953006dc
LP
111 d, e);
112
113 r = generator_write_veritysetup_service_section(f, name, u, v, roothash, options);
114 if (r < 0)
115 return r;
2f3dfc6f
LP
116
117 r = fflush_and_check(f);
118 if (r < 0)
78b408d2 119 return log_error_errno(r, "Failed to write file unit %s: %m", service);
2f3dfc6f 120
953006dc
LP
121 r = generator_add_symlink(arg_dest, "veritysetup.target", "requires", service);
122 if (r < 0)
123 return r;
2f3dfc6f
LP
124
125 return 0;
126}
127
78b408d2 128static int create_root_device(void) {
deb60ef9 129 return create_special_device("root", SYSTEMD_VERITYSETUP_SERVICE_ROOT, arg_root_hash, arg_root_data_what, arg_root_hash_what, arg_root_options);
78b408d2
MB
130}
131
132static int create_usr_device(void) {
deb60ef9 133 return create_special_device("usr", SYSTEMD_VERITYSETUP_SERVICE_USR, arg_usr_hash, arg_usr_data_what, arg_usr_hash_what, arg_usr_options);
78b408d2
MB
134}
135
2f3dfc6f
LP
136static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
137 int r;
138
ad0cdb6b 139 if (proc_cmdline_key_streq(key, "systemd.verity")) {
2f3dfc6f
LP
140
141 r = value ? parse_boolean(value) : 1;
142 if (r < 0)
143 log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value);
144 else
145 arg_enabled = r;
146
08b04ec7
GP
147 } else if (streq(key, "veritytab")) {
148
149 r = value ? parse_boolean(value) : 1;
150 if (r < 0)
151 log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value);
152 else
153 arg_read_veritytab = r;
154
ad0cdb6b 155 } else if (proc_cmdline_key_streq(key, "roothash")) {
2f3dfc6f
LP
156
157 if (proc_cmdline_value_missing(key, value))
158 return 0;
159
160 r = free_and_strdup(&arg_root_hash, value);
161 if (r < 0)
162 return log_oom();
163
ad0cdb6b 164 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) {
2f3dfc6f
LP
165
166 if (proc_cmdline_value_missing(key, value))
167 return 0;
168
78b408d2 169 r = free_and_strdup(&arg_root_data_what, value);
2f3dfc6f
LP
170 if (r < 0)
171 return log_oom();
172
ad0cdb6b 173 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) {
2f3dfc6f
LP
174
175 if (proc_cmdline_value_missing(key, value))
176 return 0;
177
78b408d2 178 r = free_and_strdup(&arg_root_hash_what, value);
2f3dfc6f
LP
179 if (r < 0)
180 return log_oom();
0141102f
GP
181
182 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_options")) {
183
184 if (proc_cmdline_value_missing(key, value))
185 return 0;
186
78b408d2
MB
187 r = free_and_strdup(&arg_root_options, value);
188 if (r < 0)
189 return log_oom();
190
191 } else if (proc_cmdline_key_streq(key, "usrhash")) {
93e0d320 192
78b408d2
MB
193 if (proc_cmdline_value_missing(key, value))
194 return 0;
195
196 r = free_and_strdup(&arg_usr_hash, value);
197 if (r < 0)
198 return log_oom();
199
200 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_data")) {
201
202 if (proc_cmdline_value_missing(key, value))
203 return 0;
204
205 r = free_and_strdup(&arg_usr_data_what, value);
206 if (r < 0)
207 return log_oom();
208
209 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_hash")) {
210
211 if (proc_cmdline_value_missing(key, value))
212 return 0;
213
214 r = free_and_strdup(&arg_usr_hash_what, value);
215 if (r < 0)
216 return log_oom();
217
218 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_options")) {
219
220 if (proc_cmdline_value_missing(key, value))
221 return 0;
222
223 r = free_and_strdup(&arg_usr_options, value);
0141102f
GP
224 if (r < 0)
225 return log_oom();
226
2f3dfc6f
LP
227 }
228
229 return 0;
230}
231
78b408d2
MB
232static int determine_device(
233 const char *name,
234 const char *hash,
235 char **data_what,
236 char **hash_what) {
237
238 sd_id128_t data_uuid, verity_uuid;
2f3dfc6f 239 _cleanup_free_ void *m = NULL;
2f3dfc6f
LP
240 size_t l;
241 int r;
242
78b408d2
MB
243 assert(name);
244 assert(data_what);
245 assert(hash_what);
2f3dfc6f 246
78b408d2 247 if (!hash)
2f3dfc6f
LP
248 return 0;
249
78b408d2 250 if (*data_what && *hash_what)
2f3dfc6f
LP
251 return 0;
252
78b408d2 253 r = unhexmem(hash, strlen(hash), &m, &l);
2f3dfc6f 254 if (r < 0)
78b408d2 255 return log_error_errno(r, "Failed to parse hash: %s", hash);
2f3dfc6f 256 if (l < sizeof(sd_id128_t)) {
78b408d2 257 log_debug("Root hash for %s is shorter than 128 bits (32 characters), ignoring for discovering verity partition.", name);
2f3dfc6f
LP
258 return 0;
259 }
260
78b408d2
MB
261 if (!*data_what) {
262 memcpy(&data_uuid, m, sizeof(data_uuid));
2f3dfc6f 263
b7416360 264 *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid));
78b408d2 265 if (!*data_what)
2f3dfc6f
LP
266 return log_oom();
267 }
268
78b408d2 269 if (!*hash_what) {
2f3dfc6f
LP
270 memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
271
b7416360 272 *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid));
78b408d2 273 if (!*hash_what)
2f3dfc6f
LP
274 return log_oom();
275 }
276
78b408d2
MB
277 log_info("Using data device %s and hash device %s for %s.", *data_what, *hash_what, name);
278
2f3dfc6f
LP
279 return 1;
280}
281
78b408d2
MB
282static int determine_devices(void) {
283 int r;
284
285 r = determine_device("root", arg_root_hash, &arg_root_data_what, &arg_root_hash_what);
286 if (r < 0)
287 return r;
288
289 return determine_device("usr", arg_usr_hash, &arg_usr_data_what, &arg_usr_hash_what);
290}
291
bf1484c7
LP
292static bool attach_in_initrd(const char *name, const char *options) {
293 assert(name);
294
295 /* Imply x-initrd.attach in case the volume name is among those defined in the Discoverable Partition
296 * Specification for partitions that we require to be mounted during the initrd → host transition,
297 * i.e. for the root fs itself, and /usr/. This mirrors similar behaviour in
298 * systemd-fstab-generator. */
299
300 return fstab_test_option(options, "x-initrd.attach\0") ||
301 STR_IN_SET(name, "root", "usr");
302}
303
deb60ef9 304static int create_veritytab_device(
08b04ec7
GP
305 const char *name,
306 const char *data_device,
307 const char *hash_device,
308 const char *roothash,
309 const char *options,
310 const char *source) {
311
312 _cleanup_free_ char *n = NULL, *dd = NULL, *du = NULL, *hd = NULL, *hu = NULL, *e = NULL,
313 *du_escaped = NULL, *hu_escaped = NULL, *name_escaped = NULL;
314 _cleanup_fclose_ FILE *f = NULL;
315 const char *dmname;
5dd02147 316 bool noauto, nofail, netdev, need_loop = false;
08b04ec7
GP
317 int r;
318
deb60ef9
LP
319 /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */
320
08b04ec7
GP
321 assert(name);
322 assert(data_device);
323 assert(hash_device);
324 assert(roothash);
325
326 noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
327 nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
328 netdev = fstab_test_option(options, "_netdev\0");
08b04ec7
GP
329
330 name_escaped = specifier_escape(name);
331 if (!name_escaped)
332 return log_oom();
333
334 e = unit_name_escape(name);
335 if (!e)
336 return log_oom();
337
338 du = fstab_node_to_udev_node(data_device);
339 if (!du)
340 return log_oom();
341
342 hu = fstab_node_to_udev_node(hash_device);
343 if (!hu)
344 return log_oom();
345
346 r = unit_name_build("systemd-veritysetup", e, ".service", &n);
347 if (r < 0)
348 return log_error_errno(r, "Failed to generate unit name: %m");
349
350 du_escaped = specifier_escape(du);
351 if (!du_escaped)
352 return log_oom();
353
354 hu_escaped = specifier_escape(hu);
355 if (!hu_escaped)
356 return log_oom();
357
358 r = unit_name_from_path(du, ".device", &dd);
359 if (r < 0)
360 return log_error_errno(r, "Failed to generate unit name: %m");
361
362 r = unit_name_from_path(hu, ".device", &hd);
363 if (r < 0)
364 return log_error_errno(r, "Failed to generate unit name: %m");
365
366 r = generator_open_unit_file(arg_dest, NULL, n, &f);
367 if (r < 0)
368 return r;
369
370 r = generator_write_veritysetup_unit_section(f, source);
371 if (r < 0)
372 return r;
373
374 if (netdev)
375 fprintf(f, "After=remote-fs-pre.target\n");
376
377 /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
bf1484c7
LP
378 if (!attach_in_initrd(name, options))
379 fprintf(f,
380 "Conflicts=umount.target\n"
381 "Before=umount.target\n");
08b04ec7
GP
382
383 if (!nofail)
384 fprintf(f,
385 "Before=%s\n",
386 netdev ? "remote-veritysetup.target" : "veritysetup.target");
387
388 if (path_startswith(du, "/dev/"))
389 fprintf(f,
390 "BindsTo=%s\n"
bf1484c7 391 "After=%s\n",
08b04ec7 392 dd, dd);
5dd02147
LP
393 else {
394 fprintf(f, "RequiresMountsFor=%s\n", du_escaped);
395 need_loop = true;
396 }
08b04ec7
GP
397
398 if (path_startswith(hu, "/dev/"))
399 fprintf(f,
400 "BindsTo=%s\n"
bf1484c7 401 "After=%s\n",
08b04ec7 402 hd, hd);
5dd02147
LP
403 else {
404 fprintf(f, "RequiresMountsFor=%s\n", hu_escaped);
405 need_loop = true;
406 }
407
408 if (need_loop)
ce49a479
LP
409 /* For loopback devices make sure to explicitly load loop.ko, as this code might run very
410 * early where device nodes created via systemd-tmpfiles-setup-dev.service might not be
411 * around yet. Hence let's sync on the module itself. */
08b04ec7 412 fprintf(f,
ce49a479
LP
413 "Wants=modprobe@loop.service\n"
414 "After=modprobe@loop.service\n");
08b04ec7
GP
415
416 r = generator_write_veritysetup_service_section(f, name, du_escaped, hu_escaped, roothash, options);
417 if (r < 0)
418 return r;
419
420 r = fflush_and_check(f);
421 if (r < 0)
422 return log_error_errno(r, "Failed to write unit file %s: %m", n);
423
424 if (!noauto) {
425 r = generator_add_symlink(arg_dest,
426 netdev ? "remote-veritysetup.target" : "veritysetup.target",
427 nofail ? "wants" : "requires", n);
428 if (r < 0)
429 return r;
430 }
431
432 dmname = strjoina("dev-mapper-", e, ".device");
433 return generator_add_symlink(arg_dest, dmname, "requires", n);
434}
435
436static int add_veritytab_devices(void) {
437 _cleanup_fclose_ FILE *f = NULL;
438 unsigned veritytab_line = 0;
439 int r;
440
441 if (!arg_read_veritytab)
442 return 0;
443
444 r = fopen_unlocked(arg_veritytab, "re", &f);
445 if (r < 0) {
446 if (errno != ENOENT)
447 log_error_errno(errno, "Failed to open %s: %m", arg_veritytab);
448 return 0;
449 }
450
451 for (;;) {
452 _cleanup_free_ char *line = NULL, *name = NULL, *data_device = NULL, *hash_device = NULL,
453 *roothash = NULL, *options = NULL;
08b04ec7 454 char *l, *data_uuid, *hash_uuid;
08b04ec7
GP
455
456 r = read_line(f, LONG_LINE_MAX, &line);
457 if (r < 0)
458 return log_error_errno(r, "Failed to read %s: %m", arg_veritytab);
459 if (r == 0)
460 break;
461
462 veritytab_line++;
463
464 l = strstrip(line);
465 if (IN_SET(l[0], 0, '#'))
466 continue;
467
71ce3ba2
YW
468 r = sscanf(l, "%ms %ms %ms %ms %ms", &name, &data_device, &hash_device, &roothash, &options);
469 if (!IN_SET(r, 4, 5)) {
08b04ec7
GP
470 log_error("Failed to parse %s:%u, ignoring.", arg_veritytab, veritytab_line);
471 continue;
472 }
473
474 data_uuid = startswith(data_device, "UUID=");
475 if (!data_uuid)
476 data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/");
477
478 hash_uuid = startswith(hash_device, "UUID=");
479 if (!hash_uuid)
480 hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/");
481
deb60ef9
LP
482 r = create_veritytab_device(
483 name,
08b04ec7
GP
484 data_device,
485 hash_device,
486 roothash,
487 options,
488 arg_veritytab);
489 if (r < 0)
490 return r;
08b04ec7
GP
491 }
492
493 return 0;
494}
495
7a44c7e3 496static int run(const char *dest, const char *dest_early, const char *dest_late) {
2f3dfc6f
LP
497 int r;
498
7a44c7e3 499 assert_se(arg_dest = dest);
2f3dfc6f 500
08b04ec7
GP
501 arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
502
2f3dfc6f 503 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
d850ad9a
YW
504 if (r < 0)
505 return log_warning_errno(r, "Failed to parse kernel command line: %m");
2f3dfc6f 506
d850ad9a
YW
507 if (!arg_enabled)
508 return 0;
2f3dfc6f 509
08b04ec7
GP
510 r = add_veritytab_devices();
511 if (r < 0)
512 return r;
513
2f3dfc6f
LP
514 r = determine_devices();
515 if (r < 0)
d850ad9a 516 return r;
2f3dfc6f 517
78b408d2
MB
518 r = create_root_device();
519 if (r < 0)
520 return r;
521
522 return create_usr_device();
2f3dfc6f 523}
d850ad9a 524
7a44c7e3 525DEFINE_MAIN_GENERATOR_FUNCTION(run);