]>
Commit | Line | Data |
---|---|---|
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 | ||
fb883e75 ZJS |
25 | #define SYSTEMD_VERITYSETUP_SERVICE "systemd-veritysetup@root.service" |
26 | ||
2cb52121 | 27 | static const char *arg_dest = NULL; |
2f3dfc6f LP |
28 | static bool arg_enabled = true; |
29 | static char *arg_root_hash = NULL; | |
30 | static char *arg_data_what = NULL; | |
31 | static char *arg_hash_what = NULL; | |
32 | ||
d850ad9a YW |
33 | STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); |
34 | STATIC_DESTRUCTOR_REGISTER(arg_data_what, freep); | |
35 | STATIC_DESTRUCTOR_REGISTER(arg_hash_what, freep); | |
36 | ||
2f3dfc6f | 37 | static int create_device(void) { |
98bad05e | 38 | _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL; |
2f3dfc6f | 39 | _cleanup_fclose_ FILE *f = NULL; |
fb883e75 | 40 | const char *to; |
2f3dfc6f LP |
41 | int r; |
42 | ||
43 | /* If all three pieces of information are missing, then verity is turned off */ | |
44 | if (!arg_root_hash && !arg_data_what && !arg_hash_what) | |
45 | return 0; | |
46 | ||
47 | /* if one of them is missing however, the data is simply incomplete and this is an error */ | |
48 | if (!arg_root_hash) | |
49 | log_error("Verity information incomplete, root hash unspecified."); | |
50 | if (!arg_data_what) | |
51 | log_error("Verity information incomplete, root data device unspecified."); | |
52 | if (!arg_hash_what) | |
53 | log_error("Verity information incomplete, root hash device unspecified."); | |
54 | ||
55 | if (!arg_root_hash || !arg_data_what || !arg_hash_what) | |
56 | return -EINVAL; | |
57 | ||
58 | log_debug("Using root verity data device %s,\n" | |
59 | " hash device %s,\n" | |
60 | " and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash); | |
61 | ||
2f3dfc6f LP |
62 | u = fstab_node_to_udev_node(arg_data_what); |
63 | if (!u) | |
64 | return log_oom(); | |
65 | v = fstab_node_to_udev_node(arg_hash_what); | |
66 | if (!v) | |
67 | return log_oom(); | |
68 | ||
98bad05e LP |
69 | u_escaped = specifier_escape(u); |
70 | if (!u_escaped) | |
71 | return log_oom(); | |
72 | v_escaped = specifier_escape(v); | |
73 | if (!v_escaped) | |
74 | return log_oom(); | |
75 | ||
2f3dfc6f LP |
76 | r = unit_name_from_path(u, ".device", &d); |
77 | if (r < 0) | |
1a012455 | 78 | return log_error_errno(r, "Failed to generate unit name: %m"); |
2f3dfc6f LP |
79 | r = unit_name_from_path(v, ".device", &e); |
80 | if (r < 0) | |
1a012455 | 81 | return log_error_errno(r, "Failed to generate unit name: %m"); |
2f3dfc6f | 82 | |
98bad05e LP |
83 | root_hash_escaped = specifier_escape(arg_root_hash); |
84 | if (!root_hash_escaped) | |
85 | return log_oom(); | |
86 | ||
fb883e75 ZJS |
87 | r = generator_open_unit_file(arg_dest, NULL, SYSTEMD_VERITYSETUP_SERVICE, &f); |
88 | if (r < 0) | |
89 | return r; | |
2f3dfc6f LP |
90 | |
91 | fprintf(f, | |
2f3dfc6f LP |
92 | "[Unit]\n" |
93 | "Description=Integrity Protection Setup for %%I\n" | |
94 | "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n" | |
95 | "SourcePath=/proc/cmdline\n" | |
96 | "DefaultDependencies=no\n" | |
97 | "Conflicts=umount.target\n" | |
98 | "BindsTo=%s %s\n" | |
99 | "IgnoreOnIsolate=true\n" | |
100 | "After=cryptsetup-pre.target %s %s\n" | |
101 | "Before=cryptsetup.target umount.target\n" | |
102 | "\n[Service]\n" | |
103 | "Type=oneshot\n" | |
104 | "RemainAfterExit=yes\n" | |
105 | "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n" | |
106 | "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n", | |
107 | d, e, | |
108 | d, e, | |
98bad05e | 109 | u_escaped, v_escaped, root_hash_escaped); |
2f3dfc6f LP |
110 | |
111 | r = fflush_and_check(f); | |
112 | if (r < 0) | |
fb883e75 | 113 | return log_error_errno(r, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE": %m"); |
2f3dfc6f | 114 | |
fb883e75 | 115 | to = strjoina(arg_dest, "/cryptsetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE); |
2f3dfc6f LP |
116 | |
117 | (void) mkdir_parents(to, 0755); | |
fb883e75 | 118 | if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE, to) < 0) |
2f3dfc6f LP |
119 | return log_error_errno(errno, "Failed to create symlink %s: %m", to); |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { | |
125 | int r; | |
126 | ||
ad0cdb6b | 127 | if (proc_cmdline_key_streq(key, "systemd.verity")) { |
2f3dfc6f LP |
128 | |
129 | r = value ? parse_boolean(value) : 1; | |
130 | if (r < 0) | |
131 | log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value); | |
132 | else | |
133 | arg_enabled = r; | |
134 | ||
ad0cdb6b | 135 | } else if (proc_cmdline_key_streq(key, "roothash")) { |
2f3dfc6f LP |
136 | |
137 | if (proc_cmdline_value_missing(key, value)) | |
138 | return 0; | |
139 | ||
140 | r = free_and_strdup(&arg_root_hash, value); | |
141 | if (r < 0) | |
142 | return log_oom(); | |
143 | ||
ad0cdb6b | 144 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_data")) { |
2f3dfc6f LP |
145 | |
146 | if (proc_cmdline_value_missing(key, value)) | |
147 | return 0; | |
148 | ||
149 | r = free_and_strdup(&arg_data_what, value); | |
150 | if (r < 0) | |
151 | return log_oom(); | |
152 | ||
ad0cdb6b | 153 | } else if (proc_cmdline_key_streq(key, "systemd.verity_root_hash")) { |
2f3dfc6f LP |
154 | |
155 | if (proc_cmdline_value_missing(key, value)) | |
156 | return 0; | |
157 | ||
158 | r = free_and_strdup(&arg_hash_what, value); | |
159 | if (r < 0) | |
160 | return log_oom(); | |
161 | } | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static int determine_devices(void) { | |
167 | _cleanup_free_ void *m = NULL; | |
168 | sd_id128_t root_uuid, verity_uuid; | |
b5ea030d | 169 | char ids[ID128_UUID_STRING_MAX]; |
2f3dfc6f LP |
170 | size_t l; |
171 | int r; | |
172 | ||
173 | /* Try to automatically derive the root data and hash device paths from the root hash */ | |
174 | ||
175 | if (!arg_root_hash) | |
176 | return 0; | |
177 | ||
178 | if (arg_data_what && arg_hash_what) | |
179 | return 0; | |
180 | ||
181 | r = unhexmem(arg_root_hash, strlen(arg_root_hash), &m, &l); | |
182 | if (r < 0) | |
183 | return log_error_errno(r, "Failed to parse root hash: %s", arg_root_hash); | |
184 | if (l < sizeof(sd_id128_t)) { | |
185 | log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition."); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | if (!arg_data_what) { | |
190 | memcpy(&root_uuid, m, sizeof(root_uuid)); | |
191 | ||
2d9b74ba | 192 | arg_data_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid, ids)); |
2f3dfc6f LP |
193 | if (!arg_data_what) |
194 | return log_oom(); | |
195 | } | |
196 | ||
197 | if (!arg_hash_what) { | |
198 | memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid)); | |
199 | ||
2d9b74ba | 200 | arg_hash_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid, ids)); |
2f3dfc6f LP |
201 | if (!arg_hash_what) |
202 | return log_oom(); | |
203 | } | |
204 | ||
205 | return 1; | |
206 | } | |
207 | ||
7a44c7e3 | 208 | static int run(const char *dest, const char *dest_early, const char *dest_late) { |
2f3dfc6f LP |
209 | int r; |
210 | ||
7a44c7e3 | 211 | assert_se(arg_dest = dest); |
2f3dfc6f | 212 | |
2f3dfc6f | 213 | r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); |
d850ad9a YW |
214 | if (r < 0) |
215 | return log_warning_errno(r, "Failed to parse kernel command line: %m"); | |
2f3dfc6f LP |
216 | |
217 | /* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab | |
218 | * or similar to define additional mappings */ | |
219 | ||
d850ad9a YW |
220 | if (!arg_enabled) |
221 | return 0; | |
2f3dfc6f LP |
222 | |
223 | r = determine_devices(); | |
224 | if (r < 0) | |
d850ad9a | 225 | return r; |
2f3dfc6f | 226 | |
7a44c7e3 | 227 | return create_device(); |
2f3dfc6f | 228 | } |
d850ad9a | 229 | |
7a44c7e3 | 230 | DEFINE_MAIN_GENERATOR_FUNCTION(run); |