]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2f3dfc6f | 2 | |
dccca82b | 3 | #include <errno.h> |
2f3dfc6f LP |
4 | #include <stdio.h> |
5 | #include <sys/stat.h> | |
6 | ||
dccca82b | 7 | #include "alloc-util.h" |
1e2f3230 | 8 | #include "cryptsetup-util.h" |
035e8e50 | 9 | #include "fileio.h" |
2f3dfc6f | 10 | #include "hexdecoct.h" |
dccca82b | 11 | #include "log.h" |
6b9306b2 | 12 | #include "main-func.h" |
035e8e50 | 13 | #include "path-util.h" |
294bf0c3 | 14 | #include "pretty-print.h" |
2f3dfc6f | 15 | #include "string-util.h" |
37ec0fdd | 16 | #include "terminal-util.h" |
2f3dfc6f LP |
17 | |
18 | static char *arg_root_hash = NULL; | |
19 | static char *arg_data_what = NULL; | |
20 | static char *arg_hash_what = NULL; | |
cb0198a1 GP |
21 | static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY; |
22 | static char *arg_root_hash_signature = NULL; | |
2f3dfc6f | 23 | |
6b9306b2 YW |
24 | STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); |
25 | STATIC_DESTRUCTOR_REGISTER(arg_data_what, freep); | |
26 | STATIC_DESTRUCTOR_REGISTER(arg_hash_what, freep); | |
cb0198a1 | 27 | STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep); |
6b9306b2 | 28 | |
2f3dfc6f | 29 | static int help(void) { |
37ec0fdd LP |
30 | _cleanup_free_ char *link = NULL; |
31 | int r; | |
32 | ||
33 | r = terminal_urlify_man("systemd-veritysetup@.service", "8", &link); | |
34 | if (r < 0) | |
35 | return log_oom(); | |
36 | ||
cb0198a1 | 37 | printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n" |
2f3dfc6f | 38 | "%s detach VOLUME\n\n" |
37ec0fdd LP |
39 | "Attaches or detaches an integrity protected block device.\n" |
40 | "\nSee the %s for details.\n" | |
41 | , program_invocation_short_name | |
42 | , program_invocation_short_name | |
43 | , link | |
44 | ); | |
2f3dfc6f LP |
45 | |
46 | return 0; | |
47 | } | |
48 | ||
cb0198a1 GP |
49 | static int looks_like_roothashsig(const char *option) { |
50 | const char *val; | |
51 | int r; | |
52 | ||
53 | if (path_is_absolute(option)) { | |
54 | ||
55 | r = free_and_strdup(&arg_root_hash_signature, option); | |
56 | if (r < 0) | |
57 | return log_oom(); | |
58 | ||
59 | return 1; | |
60 | } | |
61 | ||
62 | val = startswith(option, "base64:"); | |
63 | if (val) { | |
64 | ||
65 | r = free_and_strdup(&arg_root_hash_signature, val); | |
66 | if (r < 0) | |
67 | return log_oom(); | |
68 | ||
69 | return 1; | |
70 | } | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static int parse_options(const char *options) { | |
76 | int r; | |
77 | ||
78 | /* backward compatibility with the obsolete ROOTHASHSIG positional argument */ | |
79 | r = looks_like_roothashsig(options); | |
80 | if (r < 0) | |
81 | return r; | |
82 | if (r == 1) { | |
83 | log_warning("Usage of ROOTHASHSIG positional argument is deprecated. " | |
84 | "Please use the option root-hash-signature=%s instead.", options); | |
85 | return 0; | |
86 | } | |
87 | ||
88 | for (;;) { | |
89 | _cleanup_free_ char *word = NULL; | |
90 | char *val; | |
91 | ||
92 | r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS); | |
93 | if (r < 0) | |
94 | return log_error_errno(r, "Failed to parse options: %m"); | |
95 | if (r == 0) | |
96 | break; | |
97 | ||
08b04ec7 GP |
98 | if (STR_IN_SET(word, "noauto", "auto", "nofail", "fail", "_netdev")) |
99 | continue; | |
100 | ||
cb0198a1 GP |
101 | if (isempty(word)) |
102 | continue; | |
103 | else if (streq(word, "ignore-corruption")) | |
104 | arg_activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION; | |
105 | else if (streq(word, "restart-on-corruption")) | |
106 | arg_activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION; | |
107 | else if (streq(word, "ignore-zero-blocks")) | |
108 | arg_activate_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS; | |
109 | #ifdef CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE | |
110 | else if (streq(word, "check-at-most-once")) | |
111 | arg_activate_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE; | |
112 | #endif | |
113 | #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION | |
114 | else if (streq(word, "panic-on-corruption")) | |
115 | arg_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION; | |
116 | #endif | |
117 | else if ((val = startswith(word, "root-hash-signature="))) { | |
118 | ||
119 | r = looks_like_roothashsig(val); | |
120 | if (r < 0) | |
121 | return r; | |
122 | if (r == 0) | |
123 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "root-hash-signature expects either full path to signature file or " | |
124 | "base64 string encoding signature prefixed by base64:."); | |
125 | ||
126 | r = free_and_strdup(&arg_root_hash_signature, val); | |
127 | if (r < 0) | |
128 | return log_oom(); | |
129 | } else | |
130 | log_warning("Encountered unknown option '%s', ignoring.", word); | |
131 | } | |
132 | ||
133 | return r; | |
134 | } | |
135 | ||
6b9306b2 | 136 | static int run(int argc, char *argv[]) { |
294bd454 | 137 | _cleanup_(crypt_freep) struct crypt_device *cd = NULL; |
2f3dfc6f LP |
138 | int r; |
139 | ||
6b9306b2 YW |
140 | if (argc <= 1) |
141 | return help(); | |
2f3dfc6f | 142 | |
6b9306b2 YW |
143 | if (argc < 3) |
144 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires at least two arguments."); | |
2f3dfc6f | 145 | |
6bf3c61c | 146 | log_setup_service(); |
2f3dfc6f LP |
147 | |
148 | umask(0022); | |
149 | ||
150 | if (streq(argv[1], "attach")) { | |
151 | _cleanup_free_ void *m = NULL; | |
152 | crypt_status_info status; | |
153 | size_t l; | |
154 | ||
6b9306b2 YW |
155 | if (argc < 6) |
156 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); | |
2f3dfc6f LP |
157 | |
158 | r = unhexmem(argv[5], strlen(argv[5]), &m, &l); | |
6b9306b2 YW |
159 | if (r < 0) |
160 | return log_error_errno(r, "Failed to parse root hash: %m"); | |
2f3dfc6f LP |
161 | |
162 | r = crypt_init(&cd, argv[4]); | |
6b9306b2 YW |
163 | if (r < 0) |
164 | return log_error_errno(r, "Failed to open verity device %s: %m", argv[4]); | |
2f3dfc6f | 165 | |
efc3b12f | 166 | cryptsetup_enable_logging(cd); |
2f3dfc6f LP |
167 | |
168 | status = crypt_status(cd, argv[2]); | |
3742095b | 169 | if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) { |
2f3dfc6f | 170 | log_info("Volume %s already active.", argv[2]); |
6b9306b2 | 171 | return 0; |
2f3dfc6f LP |
172 | } |
173 | ||
cb0198a1 GP |
174 | if (argc > 6) { |
175 | r = parse_options(argv[6]); | |
176 | if (r < 0) | |
177 | return log_error_errno(r, "Failed to parse options: %m"); | |
178 | } | |
179 | ||
2f3dfc6f | 180 | r = crypt_load(cd, CRYPT_VERITY, NULL); |
6b9306b2 YW |
181 | if (r < 0) |
182 | return log_error_errno(r, "Failed to load verity superblock: %m"); | |
2f3dfc6f LP |
183 | |
184 | r = crypt_set_data_device(cd, argv[3]); | |
6b9306b2 YW |
185 | if (r < 0) |
186 | return log_error_errno(r, "Failed to configure data device: %m"); | |
2f3dfc6f | 187 | |
cb0198a1 | 188 | if (arg_root_hash_signature && *arg_root_hash_signature) { |
035e8e50 LB |
189 | #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY |
190 | _cleanup_free_ char *hash_sig = NULL; | |
191 | size_t hash_sig_size; | |
192 | char *value; | |
193 | ||
cb0198a1 | 194 | if ((value = startswith(arg_root_hash_signature, "base64:"))) { |
035e8e50 LB |
195 | r = unbase64mem(value, strlen(value), (void *)&hash_sig, &hash_sig_size); |
196 | if (r < 0) | |
cb0198a1 | 197 | return log_error_errno(r, "Failed to parse root hash signature '%s': %m", arg_root_hash_signature); |
035e8e50 | 198 | } else { |
986311c2 | 199 | r = read_full_file_full( |
cb0198a1 | 200 | AT_FDCWD, arg_root_hash_signature, UINT64_MAX, SIZE_MAX, |
986311c2 LP |
201 | READ_FULL_FILE_CONNECT_SOCKET, |
202 | NULL, | |
203 | &hash_sig, &hash_sig_size); | |
035e8e50 LB |
204 | if (r < 0) |
205 | return log_error_errno(r, "Failed to read root hash signature: %m"); | |
206 | } | |
207 | ||
cb0198a1 | 208 | r = crypt_activate_by_signed_key(cd, argv[2], m, l, hash_sig, hash_sig_size, arg_activate_flags); |
035e8e50 LB |
209 | #else |
210 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature %s requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()", argv[6]); | |
211 | #endif | |
212 | } else | |
cb0198a1 | 213 | r = crypt_activate_by_volume_key(cd, argv[2], m, l, arg_activate_flags); |
6b9306b2 YW |
214 | if (r < 0) |
215 | return log_error_errno(r, "Failed to set up verity device: %m"); | |
2f3dfc6f LP |
216 | |
217 | } else if (streq(argv[1], "detach")) { | |
218 | ||
219 | r = crypt_init_by_name(&cd, argv[2]); | |
220 | if (r == -ENODEV) { | |
221 | log_info("Volume %s already inactive.", argv[2]); | |
6b9306b2 | 222 | return 0; |
2f3dfc6f | 223 | } |
6b9306b2 YW |
224 | if (r < 0) |
225 | return log_error_errno(r, "crypt_init_by_name() failed: %m"); | |
2f3dfc6f | 226 | |
efc3b12f | 227 | cryptsetup_enable_logging(cd); |
2f3dfc6f LP |
228 | |
229 | r = crypt_deactivate(cd, argv[2]); | |
6b9306b2 YW |
230 | if (r < 0) |
231 | return log_error_errno(r, "Failed to deactivate: %m"); | |
2f3dfc6f | 232 | |
6b9306b2 YW |
233 | } else |
234 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown verb %s.", argv[1]); | |
2f3dfc6f | 235 | |
6b9306b2 | 236 | return 0; |
2f3dfc6f | 237 | } |
6b9306b2 YW |
238 | |
239 | DEFINE_MAIN_FUNCTION(run); |