From 7f5f350b62b814d2d052b60a5332b17bda31fb21 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 8 Sep 2022 12:37:52 +0200 Subject: [PATCH] libmount: (verity) rewrite to use hookset API * initialize only when relevant verity.* options detected * setup verity device by prepare-source hook * add post-mount hook to setup deferred deactivation * deactivate the device on error (when de-initialize the hookset) * call dlopen/dlclose only once, share symbols between all hooks * remove verity specific code from rest of libmount Signed-off-by: Karel Zak --- libmount/src/Makemodule.am | 2 +- libmount/src/context.c | 15 - libmount/src/context_mount.c | 4 - .../{context_veritydev.c => hook_veritydev.c} | 528 ++++++++++-------- libmount/src/hooks.c | 3 + libmount/src/mountP.h | 8 +- 6 files changed, 304 insertions(+), 256 deletions(-) rename libmount/src/{context_veritydev.c => hook_veritydev.c} (56%) diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 7e9ac3ee98..1d38cc959c 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -29,7 +29,6 @@ libmount_la_SOURCES = \ if LINUX libmount_la_SOURCES += \ libmount/src/context.c \ - libmount/src/context_veritydev.c \ libmount/src/context_mount.c \ libmount/src/context_umount.c \ libmount/src/hook_mount_legacy.c \ @@ -38,6 +37,7 @@ libmount_la_SOURCES += \ libmount/src/hook_owner.c \ libmount/src/hook_idmap.c \ libmount/src/hook_loopdev.c \ + libmount/src/hook_veritydev.c \ libmount/src/monitor.c if HAVE_BTRFS diff --git a/libmount/src/context.c b/libmount/src/context.c index b846a93035..048c0fa643 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -1853,21 +1853,6 @@ int mnt_context_prepare_srcpath(struct libmnt_context *cxt) if (rc) goto end; - /* - * Initialize verity or loop device - * ENOTSUP means verity options were requested, but the library is built without - * libcryptsetup so integrity cannot be enforced, and this should be an error - * rather than a silent fallback to a simple loopdev mount - */ - rc = mnt_context_is_veritydev(cxt); - if (rc == -ENOTSUP) { - goto end; - } else if (rc) { - rc = mnt_context_setup_veritydev(cxt); - if (rc) - goto end; - } - DBG(CXT, ul_debugobj(cxt, "final srcpath '%s'", mnt_fs_get_source(cxt->fs))); diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index 743c77a05a..907cb65711 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -896,10 +896,6 @@ int mnt_context_do_mount(struct libmnt_context *cxt) if (rc) return rc; - /* Cleanup will be immediate on failure, and deferred to umount on success */ - if (mnt_context_is_veritydev(cxt)) - mnt_context_deferred_delete_veritydev(cxt); - if (!mnt_context_switch_ns(cxt, ns_old)) return -MNT_ERR_NAMESPACE; diff --git a/libmount/src/context_veritydev.c b/libmount/src/hook_veritydev.c similarity index 56% rename from libmount/src/context_veritydev.c rename to libmount/src/hook_veritydev.c index 850f9aeaef..8f2386539c 100644 --- a/libmount/src/context_veritydev.c +++ b/libmount/src/hook_veritydev.c @@ -13,16 +13,18 @@ #include "mountP.h" -#if defined(HAVE_CRYPTSETUP) +#ifdef HAVE_CRYPTSETUP -#ifdef CRYPTSETUP_VIA_DLOPEN -#include -#endif #include #include "path.h" #include "strutils.h" +#include "fileutils.h" +#include "pathnames.h" #ifdef CRYPTSETUP_VIA_DLOPEN +# include + +/* Pointers to libcryptsetup functions (initiliazed by dlsym()) */ struct verity_opers { void (*crypt_set_debug_level)(int); void (*crypt_set_log_callback)(struct crypt_device *, void (*log)(int, const char *, void *), void *); @@ -41,7 +43,7 @@ struct verity_opers { int (*crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t); }; -/* symbols for dlopen() */ +/* libcryptsetup functions names and offsets in 'struct verity_opers' */ struct verity_sym { const char *name; size_t offset; /* offset of the symbol in verity_opers */ @@ -53,6 +55,7 @@ struct verity_sym { .offset = offsetof(struct verity_opers, _name), \ } +/* All required symbols */ static const struct verity_sym verity_symbols[] = { DEF_VERITY_SYM( crypt_set_debug_level ), @@ -71,79 +74,209 @@ static const struct verity_sym verity_symbols[] = DEF_VERITY_SYM( crypt_deactivate_by_name ), }; +#endif /* CRYPTSETUP_VIA_DLOPEN */ + + +/* Data used by all verity hooks */ +struct hookset_data { + char *devname; /* the device */ +#ifdef CRYPTSETUP_VIA_DLOPEN + void *dl; /* dlopen() */ + struct verity_opers dl_funcs; /* dlsym() */ +#endif +}; + +/* libcryptsetup call -- dlopen version requires 'struct hookset_data *hsd' */ +#ifdef CRYPTSETUP_VIA_DLOPEN +# define verity_call(_func) (hsd->dl_funcs._func) +#else +# define verity_call(_func) (_func) +#endif -static int verity_load_symbols(struct libmnt_context *cxt, void **dl, struct verity_opers *oprs) + +static void delete_veritydev(struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + struct hookset_data *hsd); + + +#ifdef CRYPTSETUP_VIA_DLOPEN +static int load_libcryptsetup_symbols(struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + struct hookset_data *hsd) { size_t i; + int flags = RTLD_LAZY | RTLD_LOCAL; - if (!dl) - return -EINVAL; - /* - * dlopen() - */ - if (!*dl) { - int flags = RTLD_LAZY | RTLD_LOCAL; + assert(cxt); + assert(hsd); + assert(hsd->dl == NULL); - /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */ + /* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */ #ifdef RTLD_NODELETE - flags |= RTLD_NODELETE; + flags |= RTLD_NODELETE; #endif - /* glibc extension: might help to avoid further symbols clashes */ + /* glibc extension: might help to avoid further symbols clashes */ #ifdef RTLD_DEEPBIND - flags |= RTLD_DEEPBIND; + flags |= RTLD_DEEPBIND; #endif - *dl = dlopen("libcryptsetup.so.12", flags); - if (!*dl) { - DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected but cannot dlopen libcryptsetup")); - return -ENOTSUP; - } + hsd->dl = dlopen("libcryptsetup.so.12", flags); + if (!hsd->dl) { + DBG(HOOK, ul_debugobj(hs, "cannot dlopen libcryptsetup")); + return -ENOTSUP; } /* clear errors first, then load all the libcryptsetup symbols */ dlerror(); - /* - * dlsym() - */ + /* dlsym() */ for (i = 0; i < ARRAY_SIZE(verity_symbols); i++) { char *errmsg; const struct verity_sym *def = &verity_symbols[i]; void **sym; - sym = (void **) ((char *) oprs + def->offset); - *sym = dlsym(*dl, def->name); + sym = (void **) ((char *) (&hsd->dl_funcs) + def->offset); + *sym = dlsym(hsd->dl, def->name); errmsg = dlerror(); if (errmsg) { - DBG(VERITY, ul_debugobj(cxt, "cannot obtain address of a '%s' symbol: %s", def->name, errmsg)); + DBG(HOOK, ul_debugobj(hs, "dlsym failed %s: %s", def->name, errmsg)); return -ENOTSUP; } } return 0; } +#endif -/* - * with dlopen -- call pointer to libcryptsetup function, the pointer is - * stored in __verity_opers and initialized by dlsym() in - * verity_load_symbols(). - */ -# define verity_call(_func) ( __verity_opers._func ) +/* libcryptsetup callback */ +static void libcryptsetup_log(int level __attribute__((__unused__)), + const char *msg, void *data) +{ + const struct libmnt_hookset *hs = (struct libmnt_hookset *) data; + DBG(HOOK, ul_debugobj(hs, "cryptsetup: %s", msg)); +} -#else /* !CRYPTSETUP_VIA_DLOPEN */ +/* free global data */ +static void free_hookset_data( struct libmnt_context *cxt, + const struct libmnt_hookset *hs) +{ + struct hookset_data *hsd = mnt_context_get_hookset_data(cxt, hs); -/* - * without dlopen -- call linked libcryptsetup function - */ -# define verity_call(_func) (_func) + if (!hsd) + return; + if (hsd->devname) + delete_veritydev(cxt, hs, hsd); +#ifdef CRYPTSETUP_VIA_DLOPEN + if (hsd->dl) + dlclose(hsd->dl); +#endif + free(hsd); + mnt_context_set_hookset_data(cxt, hs, NULL); +} -#endif /* CRYPTSETUP_VIA_DLOPEN */ +/* global data, used by all callbacks */ +static struct hookset_data *new_hookset_data( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs) +{ + struct hookset_data *hsd = calloc(1, sizeof(struct hookset_data)); + + if (hsd && mnt_context_set_hookset_data(cxt, hs, hsd) != 0) + goto failed; + +#ifdef CRYPTSETUP_VIA_DLOPEN + if (load_libcryptsetup_symbols(cxt, hs, hsd) != 0) + goto failed; +#endif + if (mnt_context_is_verbose(cxt)) + verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) ); + + verity_call( crypt_set_log_callback(NULL, libcryptsetup_log, (void *) hs) ); + + return hsd; +failed: + free(hsd); + return NULL; +} + +/* libmount callback -- cleanup all */ +static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs) +{ + DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name)); + + /* remove all our hooks */ + while (mnt_context_remove_hook(cxt, hs, 0, NULL) == 0); + + /* free and remove global hookset data */ + free_hookset_data(cxt, hs); + + return 0; +} + +/* check mount options for verity stuff */ +static int is_veritydev_required(struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + struct libmnt_optlist *ol) +{ + const char *src; + unsigned long flags = 0; + + assert(cxt); + assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + + if (cxt->action != MNT_ACT_MOUNT) + return 0; + if (!cxt->fs) + return 0; + src = mnt_fs_get_srcpath(cxt->fs); + if (!src) + return 0; /* backing file not set */ + + ol = mnt_context_get_optlist(cxt); + if (!ol) + return 0; + if (mnt_optlist_is_bind(ol) + || mnt_optlist_is_move(ol) + || mnt_context_propagation_only(cxt)) + return 0; + if (mnt_context_get_user_mflags(cxt, &flags)) + return 0; -static void libcryptsetup_log(int level __attribute__((__unused__)), const char *msg, void *data) + if (flags & (MNT_MS_HASH_DEVICE | MNT_MS_ROOT_HASH | MNT_MS_HASH_OFFSET)) { + DBG(HOOK, ul_debugobj(hs, "verity options detected")); + return 1; + } + + return 0; +} + +static void delete_veritydev(struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + struct hookset_data *hsd) { - struct libmnt_context *cxt = (struct libmnt_context *)data; + uint32_t flags = 0; + int rc; + + if (!hsd || !hsd->devname) + return; + + if (mnt_context_get_status(cxt) != 0) + /* + * mount(2) success, use deferred deactivation + */ + flags |= CRYPT_DEACTIVATE_DEFERRED; + + rc = verity_call( crypt_deactivate_by_name(NULL, hsd->devname, flags) ); + + DBG(HOOK, ul_debugobj(hs, "deleted %s [rc=%d%s]", + hsd->devname, rc, + flags & CRYPT_DEACTIVATE_DEFERRED ? " deferred" : "" )); + if (rc == 0) { + free(hsd->devname); + hsd->devname = NULL; + } - DBG(VERITY, ul_debugobj(cxt, "cryptsetup: %s", msg)); + return; } /* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */ @@ -175,34 +308,38 @@ static size_t crypt_hex_to_bytes(const char *hex, char **result) } -int mnt_context_setup_veritydev(struct libmnt_context *cxt) +static int setup_veritydev( struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + struct hookset_data *hsd, + struct libmnt_optlist *ol) { - struct libmnt_optlist *ol; struct libmnt_opt *opt; - const char *backing_file, *hash_device = NULL, *root_hash_file = NULL, *fec_device = NULL, - *root_hash_sig_file = NULL; - char *key = NULL, *root_hash_binary = NULL, *mapper_device = NULL, - *mapper_device_full = NULL, *backing_file_basename = NULL, *root_hash = NULL, - *hash_sig = NULL; + const char *backing_file, + *hash_device = NULL, + *root_hash_file = NULL, + *fec_device = NULL, + *root_hash_sig_file = NULL; + char *key = NULL, + *root_hash_binary = NULL, + *mapper_device = NULL, + *root_hash = NULL, + *hash_sig = NULL; + size_t hash_size, hash_sig_size = 0, keysize = 0; struct crypt_params_verity crypt_params = {}; struct crypt_device *crypt_dev = NULL; + int rc = 0; /* Use the same default for FEC parity bytes as cryptsetup uses */ uint64_t offset = 0, fec_offset = 0, fec_roots = 2; uint32_t crypt_activate_flags = CRYPT_ACTIVATE_READONLY; + struct stat hash_sig_st; -#ifdef CRYPTSETUP_VIA_DLOPEN - void *dl = NULL; - struct verity_opers __verity_opers = { NULL } ; /* see verity_call() macro */ -#endif + assert(cxt); assert(cxt->fs); - assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); - - ol = mnt_context_get_optlist(cxt); - if (!ol) - return -ENOMEM; + assert(hsd); + assert(hsd->devname == NULL); /* dm-verity volumes are read-only, and mount will fail if not set */ mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux); @@ -210,104 +347,91 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) backing_file = mnt_fs_get_srcpath(cxt->fs); if (!backing_file) return -EINVAL; + else { + /* To avoid clashes, prefix libmnt_ to all mapper devices */ + char *p, *path = strdup(backing_file); + if (!path) + return -ENOMEM; + + p = stripoff_last_component(path); + if (p) + mapper_device = calloc(strlen(p) + sizeof("libmnt_"), sizeof(char)); + if (mapper_device) { + strcat(mapper_device, "libmnt_"); + strcat(mapper_device, p); + } + free(path); + if (!mapper_device) + return -ENOMEM; + } - /* To avoid clashes, prefix libmnt_ to all mapper devices */ - backing_file_basename = basename(backing_file); - mapper_device = calloc(strlen(backing_file_basename) + strlen("libmnt_") + 1, sizeof(char)); - if (!mapper_device) - return -ENOMEM; - strcat(mapper_device, "libmnt_"); - strcat(mapper_device, backing_file_basename); - - DBG(VERITY, ul_debugobj(cxt, "trying to setup verity device for %s", backing_file)); + DBG(HOOK, ul_debugobj(hs, "verity: setup for %s [%s]", backing_file, mapper_device)); - /* - * verity.hashdevice= - */ + /* verity.hashdevice= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace))) hash_device = mnt_opt_get_value(opt); - /* - * verity.roothash= - */ + /* verity.roothash= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH, cxt->map_userspace))) { root_hash = strdup(mnt_opt_get_value(opt)); rc = root_hash ? 0 : -ENOMEM; } - /* - * verity.hashoffset= - */ + /* verity.hashoffset= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_OFFSET, cxt->map_userspace)) && mnt_opt_has_value(opt)) { if (strtosize(mnt_opt_get_value(opt), &offset)) { - DBG(VERITY, ul_debugobj(cxt, "failed to parse verity.hashoffset=")); + DBG(HOOK, ul_debugobj(hs, "failed to parse verity.hashoffset=")); rc = -MNT_ERR_MOUNTOPT; } } - /* - * verity.roothashfile= - */ + /* verity.roothashfile= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_FILE, cxt->map_userspace))) root_hash_file = mnt_opt_get_value(opt); - /* - * verity.fecdevice= - */ + /* verity.fecdevice= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_DEVICE, cxt->map_userspace))) fec_device = mnt_opt_get_value(opt); - /* - * verity.fecoffset= - */ + /* verity.fecoffset= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_OFFSET, cxt->map_userspace)) - && mnt_opt_has_value(opt)) { - if (strtosize(mnt_opt_get_value(opt), &fec_offset)) { - DBG(VERITY, ul_debugobj(cxt, "failed to parse verity.fecoffset=")); - rc = -MNT_ERR_MOUNTOPT; - } + && mnt_opt_has_value(opt) + && strtosize(mnt_opt_get_value(opt), &fec_offset)) { + DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecoffset=")); + rc = -MNT_ERR_MOUNTOPT; } - /* - * verity.fecroots= - */ + /* verity.fecroots= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_FEC_ROOTS, cxt->map_userspace)) - && mnt_opt_has_value(opt)) { - if (strtosize(mnt_opt_get_value(opt), &fec_roots)) { - DBG(VERITY, ul_debugobj(cxt, "failed to parse verity.fecroots=")); - rc = -MNT_ERR_MOUNTOPT; - } + && mnt_opt_has_value(opt) + && strtosize(mnt_opt_get_value(opt), &fec_roots)) { + DBG(HOOK, ul_debugobj(hs, "failed to parse verity.fecroots=")); + rc = -MNT_ERR_MOUNTOPT; } - /* - * verity.roothashsig= - */ + /* verity.roothashsig= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_ROOT_HASH_SIG, cxt->map_userspace)) && mnt_opt_has_value(opt)) { root_hash_sig_file = mnt_opt_get_value(opt); - DBG(VERITY, ul_debugobj(cxt, "checking %s", root_hash_sig_file)); + DBG(HOOK, ul_debugobj(hs, "verity: checking %s", root_hash_sig_file)); rc = ul_path_stat(NULL, &hash_sig_st, 0, root_hash_sig_file); if (rc == 0) rc = S_ISREG(hash_sig_st.st_mode) && hash_sig_st.st_size ? 0 : -EINVAL; - if (rc == 0) { hash_sig_size = hash_sig_st.st_size; hash_sig = malloc(hash_sig_size); rc = hash_sig ? 0 : -ENOMEM; } - if (rc == 0) { rc = ul_path_read(NULL, hash_sig, hash_sig_size, root_hash_sig_file); rc = rc < (int)hash_sig_size ? -1 : 0; } } - /* - * verity.oncorruption= - */ + /* verity.oncorruption= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_VERITY_ON_CORRUPTION, cxt->map_userspace)) && mnt_opt_has_value(opt)) { const char *val = mnt_opt_get_value(opt); @@ -320,16 +444,16 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) #ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION crypt_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION; #else - DBG(VERITY, ul_debugobj(cxt, "verity.oncorruption=panic not supported by libcryptsetup, ignoring")); + DBG(HOOK, ul_debugobj(hs, "verity.oncorruption=panic not supported by libcryptsetup, ignoring")); #endif else { - DBG(VERITY, ul_debugobj(cxt, "failed to parse verity.oncorruption=")); + DBG(HOOK, ul_debugobj(hs, "failed to parse verity.oncorruption=")); rc = -MNT_ERR_MOUNTOPT; } } if (!rc && root_hash && root_hash_file) { - DBG(VERITY, ul_debugobj(cxt, "verity.roothash and verity.roothashfile are mutually exclusive")); + DBG(HOOK, ul_debugobj(hs, "verity.roothash and verity.roothashfile are mutually exclusive")); rc = -EINVAL; } else if (!rc && root_hash_file) { rc = ul_path_read_string(NULL, &root_hash, root_hash_file); @@ -337,23 +461,12 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) } if (!rc && (!hash_device || !root_hash)) { - DBG(VERITY, ul_debugobj(cxt, "verity.hashdevice and one of verity.roothash or verity.roothashfile are mandatory")); + DBG(HOOK, ul_debugobj(hs, "verity.hashdevice and one of verity.roothash or verity.roothashfile are mandatory")); rc = -EINVAL; } -#ifdef CRYPTSETUP_VIA_DLOPEN - if (rc == 0) - rc = verity_load_symbols(cxt, &dl, &__verity_opers); -#endif - if (rc) - goto done; - - if (mnt_context_is_verbose(cxt)) - verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) ); - - verity_call( crypt_set_log_callback(NULL, libcryptsetup_log, cxt) ); - - rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) ); + if (!rc) + rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) ); if (rc) goto done; @@ -370,7 +483,7 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) hash_size = verity_call( crypt_get_volume_key_size(crypt_dev) ); if (crypt_hex_to_bytes(root_hash, &root_hash_binary) != hash_size) { - DBG(VERITY, ul_debugobj(cxt, "root hash %s is not of length %zu", root_hash, hash_size)); + DBG(HOOK, ul_debugobj(hs, "root hash %s is not of length %zu", root_hash, hash_size)); rc = -EINVAL; goto done; } @@ -381,7 +494,7 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) hash_sig, hash_sig_size, crypt_activate_flags) ); #else rc = -EINVAL; - DBG(VERITY, ul_debugobj(cxt, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig)); + DBG(HOOK, ul_debugobj(hs, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig)); #endif } else rc = verity_call( crypt_activate_by_volume_key(crypt_dev, mapper_device, root_hash_binary, hash_size, @@ -398,8 +511,10 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) * Pass through only OOM errors or mismatching root hash errors. */ if (rc == -EEXIST) { - DBG(VERITY, ul_debugobj(cxt, "%s already in use as /dev/mapper/%s", backing_file, mapper_device)); + DBG(HOOK, ul_debugobj(hs, "%s already in use as /dev/mapper/%s", backing_file, mapper_device)); + verity_call( crypt_free(crypt_dev) ); + rc = verity_call( crypt_init_by_name(&crypt_dev, mapper_device) ); if (!rc) { rc = verity_call( crypt_get_verity_info(crypt_dev, &crypt_params) ); @@ -415,14 +530,14 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) rc = verity_call( crypt_volume_key_get(crypt_dev, CRYPT_ANY_SLOT, key, &keysize, NULL, 0) ); } if (!rc) { - DBG(VERITY, ul_debugobj(cxt, "comparing root hash of existing device with %s", root_hash)); + DBG(HOOK, ul_debugobj(hs, "comparing root hash of existing device with %s", root_hash)); if (memcmp(key, root_hash_binary, hash_size)) { - DBG(VERITY, ul_debugobj(cxt, "existing device's hash does not match with %s", root_hash)); + DBG(HOOK, ul_debugobj(hs, "existing device's hash does not match with %s", root_hash)); rc = -EINVAL; goto done; } } else { - DBG(VERITY, ul_debugobj(cxt, "libcryptsetup does not support extracting root hash of existing device")); + DBG(HOOK, ul_debugobj(hs, "libcryptsetup does not support extracting root hash of existing device")); } } if (rc) { @@ -436,143 +551,94 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) #ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY if (!!hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE)) { rc = -EINVAL; - DBG(VERITY, ul_debugobj(cxt, "existing device and new mount have to either be both opened with signature or both without")); + DBG(HOOK, ul_debugobj(hs, "existing device and new mount have to either be both opened with signature or both without")); goto done; } #endif - DBG(VERITY, ul_debugobj(cxt, "root hash of %s matches %s, reusing device", mapper_device, root_hash)); + DBG(HOOK, ul_debugobj(hs, "root hash of %s matches %s, reusing device", mapper_device, root_hash)); } } if (!rc) { - cxt->flags |= MNT_FL_VERITYDEV_READY; - mapper_device_full = calloc(strlen(mapper_device) + strlen("/dev/mapper/") + 1, sizeof(char)); - if (!mapper_device_full) + hsd->devname = calloc(strlen(mapper_device) + + sizeof(_PATH_DEV_MAPPER) + 2, sizeof(char)); + if (!hsd->devname) rc = -ENOMEM; else { - strcat(mapper_device_full, "/dev/mapper/"); - strcat(mapper_device_full, mapper_device); - rc = mnt_fs_set_source(cxt->fs, mapper_device_full); + strcat(hsd->devname, _PATH_DEV_MAPPER "/"); + strcat(hsd->devname, mapper_device); + rc = mnt_fs_set_source(cxt->fs, hsd->devname); } } done: verity_call( crypt_free(crypt_dev) ); -#ifdef CRYPTSETUP_VIA_DLOPEN - if (dl) - dlclose(dl); -#endif + free(root_hash_binary); - free(mapper_device_full); free(mapper_device); free(root_hash); free(hash_sig); free(key); return rc; } +/* call after mount(2) */ +static int hook_mount_post( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + void *data __attribute__((__unused__))) +{ -int mnt_context_deferred_delete_veritydev(struct libmnt_context *cxt) + delete_veritydev(cxt, hs, mnt_context_get_hookset_data(cxt, hs)); + return 0; +} + +/* + * first call (first callback in this hookset) + */ +static int hook_prepare_source( + struct libmnt_context *cxt, + const struct libmnt_hookset *hs, + void *data __attribute__((__unused__))) { - const char *src; - /* If mounting failed delete immediately, otherwise setup auto cleanup for user umount */ - uint32_t flags = mnt_context_get_status(cxt) ? CRYPT_DEACTIVATE_DEFERRED : 0; -#ifdef CRYPTSETUP_VIA_DLOPEN - void *dl = NULL; - struct verity_opers __verity_opers = { NULL } ; /* see verity_call() macro */ -#endif - int rc = 0; + struct libmnt_optlist *ol; + struct hookset_data *hsd; + int rc; assert(cxt); - assert(cxt->fs); - if (!(cxt->flags & MNT_FL_VERITYDEV_READY)) + ol = mnt_context_get_optlist(cxt); + if (!ol) + return -ENOMEM; + + if (!is_veritydev_required(cxt, hs, ol)) return 0; - src = mnt_fs_get_srcpath(cxt->fs); - if (!src) - return -EINVAL; + hsd = new_hookset_data(cxt, hs); + if (!hsd) + return -ENOMEM; -#ifdef CRYPTSETUP_VIA_DLOPEN - rc = verity_load_symbols(cxt, &dl, &__verity_opers); -#endif + rc = setup_veritydev(cxt, hs, hsd, ol); if (!rc) { - if (mnt_context_is_verbose(cxt)) - verity_call( crypt_set_debug_level(CRYPT_DEBUG_ALL) ); - - verity_call( crypt_set_log_callback(NULL, libcryptsetup_log, cxt) ); - - rc = verity_call( crypt_deactivate_by_name(NULL, src, flags) ); - if (!rc) - cxt->flags &= ~MNT_FL_VERITYDEV_READY; + rc = mnt_context_append_hook(cxt, hs, + MNT_STAGE_MOUNT_POST, + NULL, hook_mount_post); + if (rc) + delete_veritydev(cxt, hs, hsd); } - -#ifdef CRYPTSETUP_VIA_DLOPEN - dlclose(dl); -#endif - DBG(VERITY, ul_debugobj(cxt, "deleted [rc=%d]", rc)); return rc; } -#else - -int mnt_context_setup_veritydev(struct libmnt_context *cxt __attribute__ ((__unused__))) -{ - return 0; -} - -int mnt_context_deferred_delete_veritydev(struct libmnt_context *cxt __attribute__ ((__unused__))) -{ - return 0; -} -#endif -int mnt_context_is_veritydev(struct libmnt_context *cxt) +const struct libmnt_hookset hookset_veritydev = { - const char *src; - unsigned long flags = 0; - struct libmnt_optlist *ol; + .name = "__veritydev", - assert(cxt); - assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); + .firststage = MNT_STAGE_PREP_SOURCE, + .firstcall = hook_prepare_source, - if (cxt->action != MNT_ACT_MOUNT) - return 0; - if (!cxt->fs) - return 0; - src = mnt_fs_get_srcpath(cxt->fs); - if (!src) - return 0; /* backing file not set */ + .deinit = hookset_deinit +}; +#endif /*HAVE_CRYPTSETUP*/ - ol = mnt_context_get_optlist(cxt); - if (!ol) - return 0; - if (mnt_optlist_is_bind(ol) - || mnt_optlist_is_move(ol) - || mnt_context_propagation_only(cxt)) - return 0; - if (mnt_context_get_user_mflags(cxt, &flags)) - return 0; - if (flags & (MNT_MS_HASH_DEVICE | MNT_MS_ROOT_HASH | MNT_MS_HASH_OFFSET)) { -#ifndef HAVE_CRYPTSETUP - DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected but libmount built without libcryptsetup")); - return -ENOTSUP; -#else - DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected")); - return 1; -#endif - } - - if (!strncmp(src, "/dev/mapper/libmnt_", strlen("/dev/mapper/libmnt_"))) { -#ifndef HAVE_CRYPTSETUP - DBG(VERITY, ul_debugobj(cxt, "veritydev prefix detected in source device but libmount built without libcryptsetup")); - return -ENOTSUP; -#else - DBG(VERITY, ul_debugobj(cxt, "veritydev prefix detected in source device")); - return 1; -#endif - } - - return 0; -} diff --git a/libmount/src/hooks.c b/libmount/src/hooks.c index 2dc44d9feb..4340ac61ca 100644 --- a/libmount/src/hooks.c +++ b/libmount/src/hooks.c @@ -26,6 +26,9 @@ static const struct libmnt_hookset *hooksets[] = { #ifdef __linux__ &hookset_loopdev, +#ifdef HAVE_CRYPTSETUP + &hookset_veritydev, +#endif &hookset_mkdir, &hookset_subdir, &hookset_mount_legacy, diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 799aeb2036..7f1636d9a9 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -316,6 +316,9 @@ extern const struct libmnt_hookset hookset_subdir; extern const struct libmnt_hookset hookset_owner; extern const struct libmnt_hookset hookset_idmap; extern const struct libmnt_hookset hookset_loopdev; +#ifdef HAVE_CRYPTSETUP +extern const struct libmnt_hookset hookset_veritydev; +#endif extern int mnt_context_deinit_hooksets(struct libmnt_context *cxt); extern const struct libmnt_hookset *mnt_context_get_hookset(struct libmnt_context *cxt, const char *name); @@ -604,11 +607,6 @@ extern int mnt_context_save_template(struct libmnt_context *cxt); extern int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs); -extern int mnt_context_is_veritydev(struct libmnt_context *cxt) - __attribute__((nonnull)); -extern int mnt_context_setup_veritydev(struct libmnt_context *cxt); -extern int mnt_context_deferred_delete_veritydev(struct libmnt_context *cxt); - extern struct libmnt_optlist *mnt_context_get_optlist(struct libmnt_context *cxt); /* tab_update.c */ -- 2.47.2