1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "cryptsetup-util.h"
10 #include "fstab-util.h"
11 #include "hexdecoct.h"
13 #include "main-func.h"
14 #include "parse-util.h"
15 #include "path-util.h"
16 #include "pretty-print.h"
17 #include "process-util.h"
18 #include "string-util.h"
19 #include "terminal-util.h"
21 static char *arg_hash
= NULL
;
22 static bool arg_superblock
= true;
23 static int arg_format
= 1;
24 static uint64_t arg_data_block_size
= 4096;
25 static uint64_t arg_hash_block_size
= 4096;
26 static uint64_t arg_data_blocks
= 0;
27 static uint64_t arg_hash_offset
= 0;
28 static void *arg_salt
= NULL
;
29 static uint64_t arg_salt_size
= 32;
30 static char *arg_uuid
= NULL
;
31 static uint32_t arg_activate_flags
= CRYPT_ACTIVATE_READONLY
;
32 static char *arg_fec_what
= NULL
;
33 static uint64_t arg_fec_offset
= 0;
34 static uint64_t arg_fec_roots
= 2;
35 static char *arg_root_hash_signature
= NULL
;
37 STATIC_DESTRUCTOR_REGISTER(arg_hash
, freep
);
38 STATIC_DESTRUCTOR_REGISTER(arg_salt
, freep
);
39 STATIC_DESTRUCTOR_REGISTER(arg_uuid
, freep
);
40 STATIC_DESTRUCTOR_REGISTER(arg_fec_what
, freep
);
41 STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature
, freep
);
43 static int help(void) {
44 _cleanup_free_
char *link
= NULL
;
47 r
= terminal_urlify_man("systemd-veritysetup@.service", "8", &link
);
51 printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
52 "%s detach VOLUME\n\n"
53 "Attach or detach a verity protected block device.\n"
54 "\nSee the %s for details.\n",
55 program_invocation_short_name
,
56 program_invocation_short_name
,
62 static int save_roothashsig_option(const char *option
, bool strict
) {
65 if (path_is_absolute(option
) || startswith(option
, "base64:")) {
66 if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
)
67 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
68 "Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
70 r
= free_and_strdup_warn(&arg_root_hash_signature
, option
);
79 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
80 "root-hash-signature= expects either full path to signature file or "
81 "base64 string encoding signature prefixed by base64:.");
84 static int parse_block_size(const char *t
, uint64_t *size
) {
88 r
= parse_size(t
, 1024, &u
);
92 if (u
< 512 || u
> (512 * 1024))
95 if ((u
% 512) != 0 || !ISPOWEROF2(u
))
103 static int parse_options(const char *options
) {
106 /* backward compatibility with the obsolete ROOTHASHSIG positional argument */
107 r
= save_roothashsig_option(options
, /* strict= */ false);
111 log_warning("Usage of ROOTHASHSIG positional argument is deprecated. "
112 "Please use the option root-hash-signature=%s instead.", options
);
117 _cleanup_free_
char *word
= NULL
;
120 r
= extract_first_word(&options
, &word
, ",", EXTRACT_DONT_COALESCE_SEPARATORS
| EXTRACT_UNESCAPE_SEPARATORS
);
122 return log_error_errno(r
, "Failed to parse options: %m");
126 if (STR_IN_SET(word
, "noauto", "auto", "nofail", "fail", "_netdev"))
131 else if (streq(word
, "ignore-corruption"))
132 arg_activate_flags
|= CRYPT_ACTIVATE_IGNORE_CORRUPTION
;
133 else if (streq(word
, "restart-on-corruption"))
134 arg_activate_flags
|= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION
;
135 else if (streq(word
, "ignore-zero-blocks"))
136 arg_activate_flags
|= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS
;
137 #ifdef CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE
138 else if (streq(word
, "check-at-most-once"))
139 arg_activate_flags
|= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE
;
141 #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
142 else if (streq(word
, "panic-on-corruption"))
143 arg_activate_flags
|= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
;
145 else if ((val
= startswith(word
, "superblock="))) {
147 r
= parse_boolean(val
);
149 return log_error_errno(r
, "Failed to parse boolean '%s': %m", word
);
152 } else if ((val
= startswith(word
, "format="))) {
154 if (!STR_IN_SET(val
, "0", "1"))
155 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "format= expects either 0 (original Chrome OS version) or "
156 "1 (modern version).");
158 arg_format
= val
[0] - '0';
159 } else if ((val
= startswith(word
, "data-block-size="))) {
162 r
= parse_block_size(val
, &sz
);
164 return log_error_errno(r
, "Failed to parse size '%s': %m", word
);
166 arg_data_block_size
= sz
;
167 } else if ((val
= startswith(word
, "hash-block-size="))) {
170 r
= parse_block_size(val
, &sz
);
172 return log_error_errno(r
, "Failed to parse size '%s': %m", word
);
174 arg_hash_block_size
= sz
;
175 } else if ((val
= startswith(word
, "data-blocks="))) {
178 r
= safe_atou64(val
, &u
);
180 return log_error_errno(r
, "Failed to parse number '%s': %m", word
);
183 } else if ((val
= startswith(word
, "hash-offset="))) {
186 r
= parse_size(val
, 1024, &off
);
188 return log_error_errno(r
, "Failed to parse offset '%s': %m", word
);
190 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "hash-offset= expects a 512-byte aligned value.");
192 arg_hash_offset
= off
;
193 } else if ((val
= startswith(word
, "salt="))) {
195 if (!string_is_safe(val
))
196 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "salt= is not valid.");
199 arg_salt
= mfree(arg_salt
);
201 } else if (streq(val
, "-")) {
202 arg_salt
= mfree(arg_salt
);
208 r
= unhexmem(val
, strlen(val
), &m
, &l
);
210 return log_error_errno(r
, "Failed to parse salt '%s': %m", word
);
212 free_and_replace(arg_salt
, m
);
215 } else if ((val
= startswith(word
, "uuid="))) {
217 r
= sd_id128_from_string(val
, NULL
);
219 return log_error_errno(r
, "Failed to parse UUID '%s': %m", word
);
221 r
= free_and_strdup(&arg_uuid
, val
);
224 } else if ((val
= startswith(word
, "hash="))) {
226 r
= free_and_strdup(&arg_hash
, val
);
229 } else if ((val
= startswith(word
, "fec-device="))) {
230 _cleanup_free_
char *what
= NULL
;
232 what
= fstab_node_to_udev_node(val
);
236 if (!path_is_absolute(what
))
237 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "fec-device= expects an absolute path.");
239 if (!path_is_normalized(what
))
240 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "fec-device= expects an normalized path.");
242 r
= free_and_strdup(&arg_fec_what
, what
);
245 } else if ((val
= startswith(word
, "fec-offset="))) {
248 r
= parse_size(val
, 1024, &off
);
250 return log_error_errno(r
, "Failed to parse offset '%s': %m", word
);
252 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "fec-offset= expects a 512-byte aligned value.");
254 arg_fec_offset
= off
;
255 } else if ((val
= startswith(word
, "fec-roots="))) {
258 r
= safe_atou64(val
, &u
);
260 return log_error_errno(r
, "Failed to parse number '%s', ignoring: %m", word
);
262 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "fec-rootfs= expects a value between 2 and 24 (including).");
265 } else if ((val
= startswith(word
, "root-hash-signature="))) {
266 r
= save_roothashsig_option(val
, /* strict= */ true);
271 log_warning("Encountered unknown option '%s', ignoring.", word
);
277 static int run(int argc
, char *argv
[]) {
278 _cleanup_(crypt_freep
) struct crypt_device
*cd
= NULL
;
282 if (argv_looks_like_help(argc
, argv
))
286 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "This program requires at least two arguments.");
290 cryptsetup_enable_logging(NULL
);
296 if (streq(verb
, "attach")) {
297 const char *volume
, *data_device
, *verity_device
, *root_hash
, *options
;
298 _cleanup_free_
void *m
= NULL
;
299 struct crypt_params_verity p
= {};
300 crypt_status_info status
;
304 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "attach requires at least four arguments.");
307 data_device
= argv
[3];
308 verity_device
= argv
[4];
310 options
= mangle_none(argc
> 6 ? argv
[6] : NULL
);
312 if (!filename_is_valid(volume
))
313 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Volume name '%s' is not valid.", volume
);
315 r
= unhexmem(root_hash
, SIZE_MAX
, &m
, &l
);
317 return log_error_errno(r
, "Failed to parse root hash: %m");
319 r
= crypt_init(&cd
, verity_device
);
321 return log_error_errno(r
, "Failed to open verity device %s: %m", verity_device
);
323 cryptsetup_enable_logging(cd
);
325 status
= crypt_status(cd
, volume
);
326 if (IN_SET(status
, CRYPT_ACTIVE
, CRYPT_BUSY
)) {
327 log_info("Volume %s already active.", volume
);
332 r
= parse_options(options
);
334 return log_error_errno(r
, "Failed to parse options: %m");
337 if (arg_superblock
) {
338 p
= (struct crypt_params_verity
) {
339 .fec_device
= arg_fec_what
,
340 .hash_area_offset
= arg_hash_offset
,
341 .fec_area_offset
= arg_fec_offset
,
342 .fec_roots
= arg_fec_roots
,
345 r
= crypt_load(cd
, CRYPT_VERITY
, &p
);
347 return log_error_errno(r
, "Failed to load verity superblock: %m");
349 p
= (struct crypt_params_verity
) {
350 .hash_name
= arg_hash
,
351 .data_device
= data_device
,
352 .fec_device
= arg_fec_what
,
354 .salt_size
= arg_salt_size
,
355 .hash_type
= arg_format
,
356 .data_block_size
= arg_data_block_size
,
357 .hash_block_size
= arg_hash_block_size
,
358 .data_size
= arg_data_blocks
,
359 .hash_area_offset
= arg_hash_offset
,
360 .fec_area_offset
= arg_fec_offset
,
361 .fec_roots
= arg_fec_roots
,
362 .flags
= CRYPT_VERITY_NO_HEADER
,
365 r
= crypt_format(cd
, CRYPT_VERITY
, NULL
, NULL
, arg_uuid
, NULL
, 0, &p
);
367 return log_error_errno(r
, "Failed to format verity superblock: %m");
370 r
= crypt_set_data_device(cd
, data_device
);
372 return log_error_errno(r
, "Failed to configure data device: %m");
374 if (arg_root_hash_signature
) {
375 #if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
376 _cleanup_free_
char *hash_sig
= NULL
;
377 size_t hash_sig_size
;
380 if ((value
= startswith(arg_root_hash_signature
, "base64:"))) {
381 r
= unbase64mem(value
, strlen(value
), (void *)&hash_sig
, &hash_sig_size
);
383 return log_error_errno(r
, "Failed to parse root hash signature '%s': %m", arg_root_hash_signature
);
385 r
= read_full_file_full(
386 AT_FDCWD
, arg_root_hash_signature
, UINT64_MAX
, SIZE_MAX
,
387 READ_FULL_FILE_CONNECT_SOCKET
,
389 &hash_sig
, &hash_sig_size
);
391 return log_error_errno(r
, "Failed to read root hash signature: %m");
394 r
= crypt_activate_by_signed_key(cd
, volume
, m
, l
, hash_sig
, hash_sig_size
, arg_activate_flags
);
396 assert_not_reached();
399 r
= crypt_activate_by_volume_key(cd
, volume
, m
, l
, arg_activate_flags
);
401 return log_error_errno(r
, "Failed to set up verity device '%s': %m", volume
);
403 } else if (streq(verb
, "detach")) {
408 if (!filename_is_valid(volume
))
409 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Volume name '%s' is not valid.", volume
);
411 r
= crypt_init_by_name(&cd
, volume
);
413 log_info("Volume %s 'already' inactive.", volume
);
417 return log_error_errno(r
, "crypt_init_by_name() for volume '%s' failed: %m", volume
);
419 cryptsetup_enable_logging(cd
);
421 r
= crypt_deactivate(cd
, volume
);
423 return log_error_errno(r
, "Failed to deactivate volume '%s': %m", volume
);
426 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unknown verb %s.", verb
);
431 DEFINE_MAIN_FUNCTION(run
);