]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/veritysetup/veritysetup-generator.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[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
a8d3315b
YW
139 assert(key);
140
141 if (streq(key, "systemd.verity")) {
2f3dfc6f
LP
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
08b04ec7
GP
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
a8d3315b 157 } else if (streq(key, "roothash")) {
2f3dfc6f
LP
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
ad0cdb6b 166 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) {
2f3dfc6f
LP
167
168 if (proc_cmdline_value_missing(key, value))
169 return 0;
170
78b408d2 171 r = free_and_strdup(&arg_root_data_what, value);
2f3dfc6f
LP
172 if (r < 0)
173 return log_oom();
174
ad0cdb6b 175 } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) {
2f3dfc6f
LP
176
177 if (proc_cmdline_value_missing(key, value))
178 return 0;
179
78b408d2 180 r = free_and_strdup(&arg_root_hash_what, value);
2f3dfc6f
LP
181 if (r < 0)
182 return log_oom();
0141102f
GP
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
78b408d2
MB
189 r = free_and_strdup(&arg_root_options, value);
190 if (r < 0)
191 return log_oom();
192
a8d3315b 193 } else if (streq(key, "usrhash")) {
93e0d320 194
78b408d2
MB
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);
0141102f
GP
226 if (r < 0)
227 return log_oom();
228
2f3dfc6f
LP
229 }
230
231 return 0;
232}
233
78b408d2
MB
234static 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;
2f3dfc6f 241 _cleanup_free_ void *m = NULL;
2f3dfc6f
LP
242 size_t l;
243 int r;
244
78b408d2
MB
245 assert(name);
246 assert(data_what);
247 assert(hash_what);
2f3dfc6f 248
78b408d2 249 if (!hash)
2f3dfc6f
LP
250 return 0;
251
78b408d2 252 if (*data_what && *hash_what)
2f3dfc6f
LP
253 return 0;
254
bdd2036e 255 r = unhexmem(hash, &m, &l);
2f3dfc6f 256 if (r < 0)
78b408d2 257 return log_error_errno(r, "Failed to parse hash: %s", hash);
2f3dfc6f 258 if (l < sizeof(sd_id128_t)) {
78b408d2 259 log_debug("Root hash for %s is shorter than 128 bits (32 characters), ignoring for discovering verity partition.", name);
2f3dfc6f
LP
260 return 0;
261 }
262
78b408d2
MB
263 if (!*data_what) {
264 memcpy(&data_uuid, m, sizeof(data_uuid));
2f3dfc6f 265
b7416360 266 *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid));
78b408d2 267 if (!*data_what)
2f3dfc6f
LP
268 return log_oom();
269 }
270
78b408d2 271 if (!*hash_what) {
2f3dfc6f
LP
272 memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
273
b7416360 274 *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid));
78b408d2 275 if (!*hash_what)
2f3dfc6f
LP
276 return log_oom();
277 }
278
78b408d2
MB
279 log_info("Using data device %s and hash device %s for %s.", *data_what, *hash_what, name);
280
2f3dfc6f
LP
281 return 1;
282}
283
78b408d2
MB
284static 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
bf1484c7
LP
294static 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
deb60ef9 306static int create_veritytab_device(
08b04ec7
GP
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;
5dd02147 318 bool noauto, nofail, netdev, need_loop = false;
08b04ec7
GP
319 int r;
320
deb60ef9
LP
321 /* Creates a systemd-veritysetup@.service instance for volumes specified in /etc/veritytab. */
322
08b04ec7
GP
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");
08b04ec7
GP
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. */
bf1484c7
LP
380 if (!attach_in_initrd(name, options))
381 fprintf(f,
382 "Conflicts=umount.target\n"
383 "Before=umount.target\n");
08b04ec7
GP
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"
bf1484c7 393 "After=%s\n",
08b04ec7 394 dd, dd);
5dd02147
LP
395 else {
396 fprintf(f, "RequiresMountsFor=%s\n", du_escaped);
397 need_loop = true;
398 }
08b04ec7
GP
399
400 if (path_startswith(hu, "/dev/"))
401 fprintf(f,
402 "BindsTo=%s\n"
bf1484c7 403 "After=%s\n",
08b04ec7 404 hd, hd);
5dd02147
LP
405 else {
406 fprintf(f, "RequiresMountsFor=%s\n", hu_escaped);
407 need_loop = true;
408 }
409
410 if (need_loop)
ce49a479
LP
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. */
08b04ec7 414 fprintf(f,
ce49a479
LP
415 "Wants=modprobe@loop.service\n"
416 "After=modprobe@loop.service\n");
08b04ec7 417
45e3406e 418 r = generator_write_veritysetup_service_section(f, name, du, hu, roothash, options);
08b04ec7
GP
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
438static 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;
0ff6ff2b 456 char *data_uuid, *hash_uuid;
08b04ec7 457
0ff6ff2b 458 r = read_stripped_line(f, LONG_LINE_MAX, &line);
08b04ec7
GP
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
0ff6ff2b 466 if (IN_SET(line[0], 0, '#'))
08b04ec7
GP
467 continue;
468
0ff6ff2b 469 r = sscanf(line, "%ms %ms %ms %ms %ms", &name, &data_device, &hash_device, &roothash, &options);
71ce3ba2 470 if (!IN_SET(r, 4, 5)) {
08b04ec7
GP
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
deb60ef9
LP
483 r = create_veritytab_device(
484 name,
08b04ec7
GP
485 data_device,
486 hash_device,
487 roothash,
488 options,
489 arg_veritytab);
490 if (r < 0)
491 return r;
08b04ec7
GP
492 }
493
494 return 0;
495}
496
7a44c7e3 497static int run(const char *dest, const char *dest_early, const char *dest_late) {
2f3dfc6f
LP
498 int r;
499
7a44c7e3 500 assert_se(arg_dest = dest);
2f3dfc6f 501
08b04ec7
GP
502 arg_veritytab = getenv("SYSTEMD_VERITYTAB") ?: "/etc/veritytab";
503
2f3dfc6f 504 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
d850ad9a
YW
505 if (r < 0)
506 return log_warning_errno(r, "Failed to parse kernel command line: %m");
2f3dfc6f 507
d850ad9a
YW
508 if (!arg_enabled)
509 return 0;
2f3dfc6f 510
08b04ec7
GP
511 r = add_veritytab_devices();
512 if (r < 0)
513 return r;
514
2f3dfc6f
LP
515 r = determine_devices();
516 if (r < 0)
d850ad9a 517 return r;
2f3dfc6f 518
78b408d2
MB
519 r = create_root_device();
520 if (r < 0)
521 return r;
522
523 return create_usr_device();
2f3dfc6f 524}
d850ad9a 525
7a44c7e3 526DEFINE_MAIN_GENERATOR_FUNCTION(run);