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