1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2019 Microsoft Corporation
6 * Copyright (C) 2022 Karel Zak <kzak@redhat.com>
8 * libmount is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
14 * Please, see comment in libmount/src/hooks.c to understand how hooks work.
18 #ifdef HAVE_CRYPTSETUP
20 #include <libcryptsetup.h>
23 #include "fileutils.h"
24 #include "pathnames.h"
26 #ifdef CRYPTSETUP_VIA_DLOPEN
29 /* Pointers to libcryptsetup functions (initiliazed by dlsym()) */
31 void (*crypt_set_debug_level
)(int);
32 void (*crypt_set_log_callback
)(struct crypt_device
*, void (*log
)(int, const char *, void *), void *);
33 int (*crypt_init_data_device
)(struct crypt_device
**, const char *, const char *);
34 int (*crypt_load
)(struct crypt_device
*, const char *, void *);
35 int (*crypt_get_volume_key_size
)(struct crypt_device
*);
36 # ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
37 int (*crypt_activate_by_signed_key
)(struct crypt_device
*, const char *, const char *, size_t, const char *, size_t, uint32_t);
39 int (*crypt_activate_by_volume_key
)(struct crypt_device
*, const char *, const char *, size_t, uint32_t);
40 void (*crypt_free
)(struct crypt_device
*);
41 int (*crypt_init_by_name
)(struct crypt_device
**, const char *);
42 int (*crypt_get_verity_info
)(struct crypt_device
*, struct crypt_params_verity
*);
43 int (*crypt_volume_key_get
)(struct crypt_device
*, int, char *, size_t *, const char *, size_t);
45 int (*crypt_deactivate_by_name
)(struct crypt_device
*, const char *, uint32_t);
48 /* libcryptsetup functions names and offsets in 'struct verity_opers' */
51 size_t offset
; /* offset of the symbol in verity_opers */
54 # define DEF_VERITY_SYM(_name) \
57 .offset = offsetof(struct verity_opers, _name), \
60 /* All required symbols */
61 static const struct verity_sym verity_symbols
[] =
63 DEF_VERITY_SYM( crypt_set_debug_level
),
64 DEF_VERITY_SYM( crypt_set_log_callback
),
65 DEF_VERITY_SYM( crypt_init_data_device
),
66 DEF_VERITY_SYM( crypt_load
),
67 DEF_VERITY_SYM( crypt_get_volume_key_size
),
68 # ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
69 DEF_VERITY_SYM( crypt_activate_by_signed_key
),
71 DEF_VERITY_SYM( crypt_activate_by_volume_key
),
72 DEF_VERITY_SYM( crypt_free
),
73 DEF_VERITY_SYM( crypt_init_by_name
),
74 DEF_VERITY_SYM( crypt_get_verity_info
),
75 DEF_VERITY_SYM( crypt_volume_key_get
),
77 DEF_VERITY_SYM( crypt_deactivate_by_name
),
79 #endif /* CRYPTSETUP_VIA_DLOPEN */
82 /* Data used by all verity hooks */
84 char *devname
; /* the device */
85 #ifdef CRYPTSETUP_VIA_DLOPEN
86 void *dl
; /* dlopen() */
87 struct verity_opers dl_funcs
; /* dlsym() */
91 /* libcryptsetup call -- dlopen version requires 'struct hookset_data *hsd' */
92 #ifdef CRYPTSETUP_VIA_DLOPEN
93 # define verity_call(_func) (hsd->dl_funcs._func)
95 # define verity_call(_func) (_func)
99 static void delete_veritydev(struct libmnt_context
*cxt
,
100 const struct libmnt_hookset
*hs
,
101 struct hookset_data
*hsd
);
104 #ifdef CRYPTSETUP_VIA_DLOPEN
105 static int load_libcryptsetup_symbols(struct libmnt_context
*cxt
,
106 const struct libmnt_hookset
*hs
,
107 struct hookset_data
*hsd
)
110 int flags
= RTLD_LAZY
| RTLD_LOCAL
;
114 assert(hsd
->dl
== NULL
);
116 /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */
118 flags
|= RTLD_NODELETE
;
120 /* glibc extension: might help to avoid further symbols clashes */
122 flags
|= RTLD_DEEPBIND
;
124 hsd
->dl
= dlopen("libcryptsetup.so.12", flags
);
126 DBG(HOOK
, ul_debugobj(hs
, "cannot dlopen libcryptsetup"));
130 /* clear errors first, then load all the libcryptsetup symbols */
134 for (i
= 0; i
< ARRAY_SIZE(verity_symbols
); i
++) {
136 const struct verity_sym
*def
= &verity_symbols
[i
];
139 sym
= (void **) ((char *) (&hsd
->dl_funcs
) + def
->offset
);
140 *sym
= dlsym(hsd
->dl
, def
->name
);
144 DBG(HOOK
, ul_debugobj(hs
, "dlsym failed %s: %s", def
->name
, errmsg
));
152 /* libcryptsetup callback */
153 static void libcryptsetup_log(int level
__attribute__((__unused__
)),
154 const char *msg
, void *data
)
156 const struct libmnt_hookset
*hs
= (struct libmnt_hookset
*) data
;
157 DBG(HOOK
, ul_debugobj(hs
, "cryptsetup: %s", msg
));
160 /* free global data */
161 static void free_hookset_data( struct libmnt_context
*cxt
,
162 const struct libmnt_hookset
*hs
)
164 struct hookset_data
*hsd
= mnt_context_get_hookset_data(cxt
, hs
);
169 delete_veritydev(cxt
, hs
, hsd
);
170 #ifdef CRYPTSETUP_VIA_DLOPEN
175 mnt_context_set_hookset_data(cxt
, hs
, NULL
);
178 /* global data, used by all callbacks */
179 static struct hookset_data
*new_hookset_data(
180 struct libmnt_context
*cxt
,
181 const struct libmnt_hookset
*hs
)
183 struct hookset_data
*hsd
= calloc(1, sizeof(struct hookset_data
));
185 if (hsd
&& mnt_context_set_hookset_data(cxt
, hs
, hsd
) != 0)
188 #ifdef CRYPTSETUP_VIA_DLOPEN
189 if (load_libcryptsetup_symbols(cxt
, hs
, hsd
) != 0)
192 if (mnt_context_is_verbose(cxt
))
193 verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL
) );
195 verity_call( crypt_set_log_callback(NULL
, libcryptsetup_log
, (void *) hs
) );
203 /* libmount callback -- cleanup all */
204 static int hookset_deinit(struct libmnt_context
*cxt
, const struct libmnt_hookset
*hs
)
206 DBG(HOOK
, ul_debugobj(hs
, "deinit '%s'", hs
->name
));
208 /* remove all our hooks */
209 while (mnt_context_remove_hook(cxt
, hs
, 0, NULL
) == 0);
211 /* free and remove global hookset data */
212 free_hookset_data(cxt
, hs
);
217 /* check mount options for verity stuff */
218 static int is_veritydev_required(struct libmnt_context
*cxt
,
219 const struct libmnt_hookset
*hs
,
220 struct libmnt_optlist
*ol
)
223 unsigned long flags
= 0;
226 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
228 if (cxt
->action
!= MNT_ACT_MOUNT
)
232 src
= mnt_fs_get_srcpath(cxt
->fs
);
234 return 0; /* backing file not set */
236 ol
= mnt_context_get_optlist(cxt
);
239 if (mnt_optlist_is_bind(ol
)
240 || mnt_optlist_is_move(ol
)
241 || mnt_context_propagation_only(cxt
))
244 if (mnt_context_get_user_mflags(cxt
, &flags
))
247 if (flags
& (MNT_MS_HASH_DEVICE
| MNT_MS_ROOT_HASH
| MNT_MS_HASH_OFFSET
)) {
248 DBG(HOOK
, ul_debugobj(hs
, "verity options detected"));
255 static void delete_veritydev(struct libmnt_context
*cxt
,
256 const struct libmnt_hookset
*hs
,
257 struct hookset_data
*hsd
)
262 if (!hsd
|| !hsd
->devname
)
265 if (mnt_context_get_status(cxt
) != 0)
267 * mount(2) success, use deferred deactivation
269 flags
|= CRYPT_DEACTIVATE_DEFERRED
;
271 rc
= verity_call( crypt_deactivate_by_name(NULL
, hsd
->devname
, flags
) );
273 DBG(HOOK
, ul_debugobj(hs
, "deleted %s [rc=%d%s]",
275 flags
& CRYPT_DEACTIVATE_DEFERRED
? " deferred" : "" ));
284 /* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */
285 static size_t crypt_hex_to_bytes(const char *hex
, char **result
)
287 char buf
[3] = "xx\0", *endp
, *bytes
;
299 for (i
= 0; i
< len
; i
++) {
300 memcpy(buf
, &hex
[i
* 2], 2);
302 bytes
[i
] = strtoul(buf
, &endp
, 16);
303 if (errno
|| endp
!= &buf
[2]) {
313 static int setup_veritydev( struct libmnt_context
*cxt
,
314 const struct libmnt_hookset
*hs
,
315 struct hookset_data
*hsd
,
316 struct libmnt_optlist
*ol
)
318 struct libmnt_opt
*opt
;
319 const char *backing_file
,
321 *root_hash_file
= NULL
,
323 *root_hash_sig_file
= NULL
;
325 *root_hash_binary
= NULL
,
326 *mapper_device
= NULL
,
330 size_t hash_size
, hash_sig_size
= 0, keysize
= 0;
331 struct crypt_params_verity crypt_params
= {};
332 struct crypt_device
*crypt_dev
= NULL
;
335 /* Use the same default for FEC parity bytes as cryptsetup uses */
336 uint64_t offset
= 0, fec_offset
= 0, fec_roots
= 2;
337 uint32_t crypt_activate_flags
= CRYPT_ACTIVATE_READONLY
;
339 struct stat hash_sig_st
;
344 assert(hsd
->devname
== NULL
);
346 /* dm-verity volumes are read-only, and mount will fail if not set */
347 mnt_optlist_append_flags(ol
, MS_RDONLY
, cxt
->map_linux
);
349 backing_file
= mnt_fs_get_srcpath(cxt
->fs
);
353 DBG(HOOK
, ul_debugobj(hs
, "verity: setup for %s", backing_file
));
355 /* verity.hashdevice= */
356 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_HASH_DEVICE
, cxt
->map_userspace
)))
357 hash_device
= mnt_opt_get_value(opt
);
359 /* verity.roothash= */
360 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_ROOT_HASH
, cxt
->map_userspace
))) {
361 root_hash
= strdup(mnt_opt_get_value(opt
));
362 rc
= root_hash
? 0 : -ENOMEM
;
365 /* verity.hashoffset= */
366 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_HASH_OFFSET
, cxt
->map_userspace
))
367 && mnt_opt_has_value(opt
)) {
368 if (strtosize(mnt_opt_get_value(opt
), &offset
)) {
369 DBG(HOOK
, ul_debugobj(hs
, "failed to parse verity.hashoffset="));
370 rc
= -MNT_ERR_MOUNTOPT
;
374 /* verity.roothashfile= */
375 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_ROOT_HASH_FILE
, cxt
->map_userspace
)))
376 root_hash_file
= mnt_opt_get_value(opt
);
378 /* verity.fecdevice= */
379 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_FEC_DEVICE
, cxt
->map_userspace
)))
380 fec_device
= mnt_opt_get_value(opt
);
382 /* verity.fecoffset= */
383 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_FEC_OFFSET
, cxt
->map_userspace
))
384 && mnt_opt_has_value(opt
)
385 && strtosize(mnt_opt_get_value(opt
), &fec_offset
)) {
386 DBG(HOOK
, ul_debugobj(hs
, "failed to parse verity.fecoffset="));
387 rc
= -MNT_ERR_MOUNTOPT
;
390 /* verity.fecroots= */
391 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_FEC_ROOTS
, cxt
->map_userspace
))
392 && mnt_opt_has_value(opt
)
393 && strtosize(mnt_opt_get_value(opt
), &fec_roots
)) {
394 DBG(HOOK
, ul_debugobj(hs
, "failed to parse verity.fecroots="));
395 rc
= -MNT_ERR_MOUNTOPT
;
398 /* verity.roothashsig= */
399 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_ROOT_HASH_SIG
, cxt
->map_userspace
))
400 && mnt_opt_has_value(opt
)) {
401 root_hash_sig_file
= mnt_opt_get_value(opt
);
403 DBG(HOOK
, ul_debugobj(hs
, "verity: checking %s", root_hash_sig_file
));
405 rc
= ul_path_stat(NULL
, &hash_sig_st
, 0, root_hash_sig_file
);
407 rc
= S_ISREG(hash_sig_st
.st_mode
) && hash_sig_st
.st_size
? 0 : -EINVAL
;
409 hash_sig_size
= hash_sig_st
.st_size
;
410 hash_sig
= malloc(hash_sig_size
);
411 rc
= hash_sig
? 0 : -ENOMEM
;
414 rc
= ul_path_read(NULL
, hash_sig
, hash_sig_size
, root_hash_sig_file
);
415 rc
= rc
< (int)hash_sig_size
? -1 : 0;
419 /* verity.oncorruption= */
420 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_VERITY_ON_CORRUPTION
, cxt
->map_userspace
))
421 && mnt_opt_has_value(opt
)) {
422 const char *val
= mnt_opt_get_value(opt
);
423 if (!strcmp(val
, "ignore"))
424 crypt_activate_flags
|= CRYPT_ACTIVATE_IGNORE_CORRUPTION
;
425 else if (!strcmp(val
, "restart"))
426 crypt_activate_flags
|= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION
;
427 else if (!strcmp(val
, "panic"))
428 /* Added by libcryptsetup v2.3.4 - ignore on lower versions, as with other optional features */
429 #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
430 crypt_activate_flags
|= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
;
432 DBG(HOOK
, ul_debugobj(hs
, "verity.oncorruption=panic not supported by libcryptsetup, ignoring"));
435 DBG(HOOK
, ul_debugobj(hs
, "failed to parse verity.oncorruption="));
436 rc
= -MNT_ERR_MOUNTOPT
;
440 if (!rc
&& root_hash
&& root_hash_file
) {
441 DBG(HOOK
, ul_debugobj(hs
, "verity.roothash and verity.roothashfile are mutually exclusive"));
443 } else if (!rc
&& root_hash_file
) {
444 rc
= ul_path_read_string(NULL
, &root_hash
, root_hash_file
);
445 rc
= rc
< 1 ? rc
: 0;
448 if (!rc
&& (!hash_device
|| !root_hash
)) {
449 DBG(HOOK
, ul_debugobj(hs
, "verity.hashdevice and one of verity.roothash or verity.roothashfile are mandatory"));
453 /* To avoid clashes, use the roothash as the device name. This allows us to reuse already open devices, saving
454 * a lot of time and resources when there are duplicated mounts. If the roothash is the same, then the volumes
455 * are also guaranteed to be identical. This is what systemd also does, so we can deduplicate across the whole
457 if (asprintf(&mapper_device
, "%s-verity", root_hash
) < 0)
461 rc
= verity_call( crypt_init_data_device(&crypt_dev
, hash_device
, backing_file
) );
465 memset(&crypt_params
, 0, sizeof(struct crypt_params_verity
));
466 crypt_params
.hash_area_offset
= offset
;
467 crypt_params
.fec_area_offset
= fec_offset
;
468 crypt_params
.fec_roots
= fec_roots
;
469 crypt_params
.fec_device
= fec_device
;
470 crypt_params
.flags
= 0;
472 rc
= verity_call( crypt_load(crypt_dev
, CRYPT_VERITY
, &crypt_params
) );
476 hash_size
= verity_call( crypt_get_volume_key_size(crypt_dev
) );
477 if (crypt_hex_to_bytes(root_hash
, &root_hash_binary
) != hash_size
) {
478 DBG(HOOK
, ul_debugobj(hs
, "root hash %s is not of length %zu", root_hash
, hash_size
));
484 #ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
485 rc
= verity_call( crypt_activate_by_signed_key(crypt_dev
, mapper_device
, root_hash_binary
, hash_size
,
486 hash_sig
, hash_sig_size
, crypt_activate_flags
) );
489 DBG(HOOK
, ul_debugobj(hs
, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig
));
492 rc
= verity_call( crypt_activate_by_volume_key(crypt_dev
, mapper_device
, root_hash_binary
, hash_size
,
493 crypt_activate_flags
) );
496 * If the mapper device already exists, and if libcryptsetup supports it, get the root
497 * hash associated with the existing one and compare it with the parameter passed by
498 * the user. If they match, then we can be sure the user intended to mount the exact
499 * same device, and simply reuse it and return success. Although we use the roothash
500 * as the device mapper name, and root privileges are required to open them, better be
501 * safe than sorry, so double check that the actual root hash used matches.
502 * The kernel does the refcounting for us.
503 * If libcryptsetup does not support getting the root hash out of an existing device,
504 * then return an error and tell the user that the device is already in use.
505 * Pass through only OOM errors or mismatching root hash errors.
508 DBG(HOOK
, ul_debugobj(hs
, "%s already in use as /dev/mapper/%s", backing_file
, mapper_device
));
510 verity_call( crypt_free(crypt_dev
) );
512 rc
= verity_call( crypt_init_by_name(&crypt_dev
, mapper_device
) );
514 rc
= verity_call( crypt_get_verity_info(crypt_dev
, &crypt_params
) );
516 key
= calloc(hash_size
, 1);
524 rc
= verity_call( crypt_volume_key_get(crypt_dev
, CRYPT_ANY_SLOT
, key
, &keysize
, NULL
, 0) );
527 DBG(HOOK
, ul_debugobj(hs
, "comparing root hash of existing device with %s", root_hash
));
528 if (memcmp(key
, root_hash_binary
, hash_size
)) {
529 DBG(HOOK
, ul_debugobj(hs
, "existing device's hash does not match with %s", root_hash
));
534 DBG(HOOK
, ul_debugobj(hs
, "libcryptsetup does not support extracting root hash of existing device"));
541 * Ensure that, if signatures are supported, we only reuse the device if the previous mount
542 * used the same settings, so that a previous unsigned mount will not be reused if the user
543 * asks to use signing for the new one, and viceversa.
545 #ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
546 if (!!hash_sig
!= !!(crypt_params
.flags
& CRYPT_VERITY_ROOT_HASH_SIGNATURE
)) {
548 DBG(HOOK
, ul_debugobj(hs
, "existing device and new mount have to either be both opened with signature or both without"));
552 DBG(HOOK
, ul_debugobj(hs
, "root hash of %s matches %s, reusing device", mapper_device
, root_hash
));
557 if (asprintf(&hsd
->devname
, _PATH_DEV_MAPPER
"/%s", mapper_device
) == -1)
560 rc
= mnt_fs_set_source(cxt
->fs
, hsd
->devname
);
564 verity_call( crypt_free(crypt_dev
) );
566 free(root_hash_binary
);
573 /* call after mount(2) */
574 static int hook_mount_post(
575 struct libmnt_context
*cxt
,
576 const struct libmnt_hookset
*hs
,
577 void *data
__attribute__((__unused__
)))
580 delete_veritydev(cxt
, hs
, mnt_context_get_hookset_data(cxt
, hs
));
585 * first call (first callback in this hookset)
587 static int hook_prepare_source(
588 struct libmnt_context
*cxt
,
589 const struct libmnt_hookset
*hs
,
590 void *data
__attribute__((__unused__
)))
592 struct libmnt_optlist
*ol
;
593 struct hookset_data
*hsd
;
598 ol
= mnt_context_get_optlist(cxt
);
602 if (!is_veritydev_required(cxt
, hs
, ol
))
605 hsd
= new_hookset_data(cxt
, hs
);
609 rc
= setup_veritydev(cxt
, hs
, hsd
, ol
);
611 rc
= mnt_context_append_hook(cxt
, hs
,
612 MNT_STAGE_MOUNT_POST
,
613 NULL
, hook_mount_post
);
615 delete_veritydev(cxt
, hs
, hsd
);
621 const struct libmnt_hookset hookset_veritydev
=
623 .name
= "__veritydev",
625 .firststage
= MNT_STAGE_PREP_SOURCE
,
626 .firstcall
= hook_prepare_source
,
628 .deinit
= hookset_deinit
630 #endif /*HAVE_CRYPTSETUP*/