]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/veritysetup/veritysetup-generator.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / veritysetup / veritysetup-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
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"
13 #include "generator.h"
14 #include "hexdecoct.h"
15 #include "id128-util.h"
16 #include "main-func.h"
17 #include "mkdir.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"
24
25 #define SYSTEMD_VERITYSETUP_SERVICE_ROOT "systemd-veritysetup@root.service"
26 #define SYSTEMD_VERITYSETUP_SERVICE_USR "systemd-veritysetup@usr.service"
27
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;
40
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);
49
50 static int create_special_device(
51 const char *name,
52 const char *service,
53 const char *roothash,
54 const char *data_what,
55 const char *hash_what,
56 const char *options) {
57
58 _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL;
59 _cleanup_fclose_ FILE *f = NULL;
60 int r;
61
62 /* Creates a systemd-veritysetup@.service instance for the special kernel cmdline specified root + usr devices. */
63
64 assert(name);
65 assert(service);
66
67 /* If all three pieces of information are missing, then verity is turned off */
68 if (!roothash && !data_what && !hash_what)
69 return 0;
70
71 /* if one of them is missing however, the data is simply incomplete and this is an error */
72 if (!roothash)
73 log_error("Verity information for %s incomplete, root hash unspecified.", name);
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
79 if (!roothash || !data_what || !hash_what)
80 return -EINVAL;
81
82 log_debug("Using %s verity data device %s, hash device %s, options %s, and hash %s.", name, data_what, hash_what, options, roothash);
83
84 u = fstab_node_to_udev_node(data_what);
85 if (!u)
86 return log_oom();
87 v = fstab_node_to_udev_node(hash_what);
88 if (!v)
89 return log_oom();
90
91 r = unit_name_from_path(u, ".device", &d);
92 if (r < 0)
93 return log_error_errno(r, "Failed to generate unit name: %m");
94 r = unit_name_from_path(v, ".device", &e);
95 if (r < 0)
96 return log_error_errno(r, "Failed to generate unit name: %m");
97
98 r = generator_open_unit_file(arg_dest, NULL, service, &f);
99 if (r < 0)
100 return r;
101
102 r = generator_write_veritysetup_unit_section(f, "/proc/cmdline");
103 if (r < 0)
104 return r;
105
106 fprintf(f,
107 "Before=veritysetup.target\n"
108 "BindsTo=%s %s\n"
109 "After=%s %s\n",
110 d, e,
111 d, e);
112
113 r = generator_write_veritysetup_service_section(f, name, u, v, roothash, options);
114 if (r < 0)
115 return r;
116
117 r = fflush_and_check(f);
118 if (r < 0)
119 return log_error_errno(r, "Failed to write file unit %s: %m", service);
120
121 r = generator_add_symlink(arg_dest, "veritysetup.target", "requires", service);
122 if (r < 0)
123 return r;
124
125 return 0;
126 }
127
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);
130 }
131
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);
134 }
135
136 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
137 int r;
138
139 assert(key);
140
141 if (streq(key, "systemd.verity")) {
142
143 r = value ? parse_boolean(value) : 1;
144 if (r < 0)
145 log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value);
146 else
147 arg_enabled = r;
148
149 } else if (streq(key, "veritytab")) {
150
151 r = value ? parse_boolean(value) : 1;
152 if (r < 0)
153 log_warning("Failed to parse veritytab= kernel command line switch %s. Ignoring.", value);
154 else
155 arg_read_veritytab = r;
156
157 } else if (streq(key, "roothash")) {
158
159 if (proc_cmdline_value_missing(key, value))
160 return 0;
161
162 r = free_and_strdup(&arg_root_hash, value);
163 if (r < 0)
164 return log_oom();
165
166 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) {
167
168 if (proc_cmdline_value_missing(key, value))
169 return 0;
170
171 r = free_and_strdup(&arg_root_data_what, value);
172 if (r < 0)
173 return log_oom();
174
175 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) {
176
177 if (proc_cmdline_value_missing(key, value))
178 return 0;
179
180 r = free_and_strdup(&arg_root_hash_what, value);
181 if (r < 0)
182 return log_oom();
183
184 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_options")) {
185
186 if (proc_cmdline_value_missing(key, value))
187 return 0;
188
189 r = free_and_strdup(&arg_root_options, value);
190 if (r < 0)
191 return log_oom();
192
193 } else if (streq(key, "usrhash")) {
194
195 if (proc_cmdline_value_missing(key, value))
196 return 0;
197
198 r = free_and_strdup(&arg_usr_hash, value);
199 if (r < 0)
200 return log_oom();
201
202 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_data")) {
203
204 if (proc_cmdline_value_missing(key, value))
205 return 0;
206
207 r = free_and_strdup(&arg_usr_data_what, value);
208 if (r < 0)
209 return log_oom();
210
211 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_hash")) {
212
213 if (proc_cmdline_value_missing(key, value))
214 return 0;
215
216 r = free_and_strdup(&arg_usr_hash_what, value);
217 if (r < 0)
218 return log_oom();
219
220 } else if (proc_cmdline_key_streq(key, "systemd.verity_usr_options")) {
221
222 if (proc_cmdline_value_missing(key, value))
223 return 0;
224
225 r = free_and_strdup(&arg_usr_options, value);
226 if (r < 0)
227 return log_oom();
228
229 }
230
231 return 0;
232 }
233
234 static int determine_device(
235 const char *name,
236 const char *hash,
237 char **data_what,
238 char **hash_what) {
239
240 sd_id128_t data_uuid, verity_uuid;
241 _cleanup_free_ void *m = NULL;
242 size_t l;
243 int r;
244
245 assert(name);
246 assert(data_what);
247 assert(hash_what);
248
249 if (!hash)
250 return 0;
251
252 if (*data_what && *hash_what)
253 return 0;
254
255 r = unhexmem(hash, &m, &l);
256 if (r < 0)
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);
260 return 0;
261 }
262
263 if (!*data_what) {
264 memcpy(&data_uuid, m, sizeof(data_uuid));
265
266 *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid));
267 if (!*data_what)
268 return log_oom();
269 }
270
271 if (!*hash_what) {
272 memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
273
274 *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid));
275 if (!*hash_what)
276 return log_oom();
277 }
278
279 log_info("Using data device %s and hash device %s for %s.", *data_what, *hash_what, name);
280
281 return 1;
282 }
283
284 static int determine_devices(void) {
285 int r;
286
287 r = determine_device("root", arg_root_hash, &arg_root_data_what, &arg_root_hash_what);
288 if (r < 0)
289 return r;
290
291 return determine_device("usr", arg_usr_hash, &arg_usr_data_what, &arg_usr_hash_what);
292 }
293
294 static bool attach_in_initrd(const char *name, const char *options) {
295 assert(name);
296
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. */
301
302 return fstab_test_option(options, "x-initrd.attach\0") ||
303 STR_IN_SET(name, "root", "usr");
304 }
305
306 static int create_veritytab_device(
307 const char *name,
308 const char *data_device,
309 const char *hash_device,
310 const char *roothash,
311 const char *options,
312 const char *source) {
313
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;
317 const char *dmname;
318 bool noauto, nofail, netdev, need_loop = false;
319 int r;
320
321 /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */
322
323 assert(name);
324 assert(data_device);
325 assert(hash_device);
326 assert(roothash);
327
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");
331
332 name_escaped = specifier_escape(name);
333 if (!name_escaped)
334 return log_oom();
335
336 e = unit_name_escape(name);
337 if (!e)
338 return log_oom();
339
340 du = fstab_node_to_udev_node(data_device);
341 if (!du)
342 return log_oom();
343
344 hu = fstab_node_to_udev_node(hash_device);
345 if (!hu)
346 return log_oom();
347
348 r = unit_name_build("systemd-veritysetup", e, ".service", &n);
349 if (r < 0)
350 return log_error_errno(r, "Failed to generate unit name: %m");
351
352 du_escaped = specifier_escape(du);
353 if (!du_escaped)
354 return log_oom();
355
356 hu_escaped = specifier_escape(hu);
357 if (!hu_escaped)
358 return log_oom();
359
360 r = unit_name_from_path(du, ".device", &dd);
361 if (r < 0)
362 return log_error_errno(r, "Failed to generate unit name: %m");
363
364 r = unit_name_from_path(hu, ".device", &hd);
365 if (r < 0)
366 return log_error_errno(r, "Failed to generate unit name: %m");
367
368 r = generator_open_unit_file(arg_dest, NULL, n, &f);
369 if (r < 0)
370 return r;
371
372 r = generator_write_veritysetup_unit_section(f, source);
373 if (r < 0)
374 return r;
375
376 if (netdev)
377 fprintf(f, "After=remote-fs-pre.target\n");
378
379 /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
380 if (!attach_in_initrd(name, options))
381 fprintf(f,
382 "Conflicts=umount.target\n"
383 "Before=umount.target\n");
384
385 if (!nofail)
386 fprintf(f,
387 "Before=%s\n",
388 netdev ? "remote-veritysetup.target" : "veritysetup.target");
389
390 if (path_startswith(du, "/dev/"))
391 fprintf(f,
392 "BindsTo=%s\n"
393 "After=%s\n",
394 dd, dd);
395 else {
396 fprintf(f, "RequiresMountsFor=%s\n", du_escaped);
397 need_loop = true;
398 }
399
400 if (path_startswith(hu, "/dev/"))
401 fprintf(f,
402 "BindsTo=%s\n"
403 "After=%s\n",
404 hd, hd);
405 else {
406 fprintf(f, "RequiresMountsFor=%s\n", hu_escaped);
407 need_loop = true;
408 }
409
410 if (need_loop)
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. */
414 fprintf(f,
415 "Wants=modprobe@loop.service\n"
416 "After=modprobe@loop.service\n");
417
418 r = generator_write_veritysetup_service_section(f, name, du, hu, roothash, options);
419 if (r < 0)
420 return r;
421
422 r = fflush_and_check(f);
423 if (r < 0)
424 return log_error_errno(r, "Failed to write unit file %s: %m", n);
425
426 if (!noauto) {
427 r = generator_add_symlink(arg_dest,
428 netdev ? "remote-veritysetup.target" : "veritysetup.target",
429 nofail ? "wants" : "requires", n);
430 if (r < 0)
431 return r;
432 }
433
434 dmname = strjoina("dev-mapper-", e, ".device");
435 return generator_add_symlink(arg_dest, dmname, "requires", n);
436 }
437
438 static int add_veritytab_devices(void) {
439 _cleanup_fclose_ FILE *f = NULL;
440 unsigned veritytab_line = 0;
441 int r;
442
443 if (!arg_read_veritytab)
444 return 0;
445
446 r = fopen_unlocked(arg_veritytab, "re", &f);
447 if (r < 0) {
448 if (errno != ENOENT)
449 log_error_errno(errno, "Failed to open %s: %m", arg_veritytab);
450 return 0;
451 }
452
453 for (;;) {
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;
457
458 r = read_stripped_line(f, LONG_LINE_MAX, &line);
459 if (r < 0)
460 return log_error_errno(r, "Failed to read %s: %m", arg_veritytab);
461 if (r == 0)
462 break;
463
464 veritytab_line++;
465
466 if (IN_SET(line[0], 0, '#'))
467 continue;
468
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);
472 continue;
473 }
474
475 data_uuid = startswith(data_device, "UUID=");
476 if (!data_uuid)
477 data_uuid = path_startswith(data_device, "/dev/disk/by-uuid/");
478
479 hash_uuid = startswith(hash_device, "UUID=");
480 if (!hash_uuid)
481 hash_uuid = path_startswith(hash_device, "/dev/disk/by-uuid/");
482
483 r = create_veritytab_device(
484 name,
485 data_device,
486 hash_device,
487 roothash,
488 options,
489 arg_veritytab);
490 if (r < 0)
491 return r;
492 }
493
494 return 0;
495 }
496
497 static int run(const char *dest, const char *dest_early, const char *dest_late) {
498 int r;
499
500 assert_se(arg_dest = dest);
501
502 arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
503
504 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
505 if (r < 0)
506 return log_warning_errno(r, "Failed to parse kernel command line: %m");
507
508 if (!arg_enabled)
509 return 0;
510
511 r = add_veritytab_devices();
512 if (r < 0)
513 return r;
514
515 r = determine_devices();
516 if (r < 0)
517 return r;
518
519 r = create_root_device();
520 if (r < 0)
521 return r;
522
523 return create_usr_device();
524 }
525
526 DEFINE_MAIN_GENERATOR_FUNCTION(run);