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