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