cryptsetup: add option to use via dlopen in libmount
Enabling libcrypsetup in libmount had several unintended side effects. First of all, it increases the Debian minimal image size by ~2.5% (5.6MB worth of new libraries). Then, due to libcryptsetup linkage to OpenSSL and libjson-c, it causes incompatibilities with external programs linking against both libmount and a private, static, old version of OpenSSL, or external programs linking against libjansson or json-glib, which have one symbol in common with libjson-c. If ./configure is ran with --with-crypsetup=dlopen, instead of linking to libcrypsetup, use dlopen to resolve the symbols at runtime only when the verity feature is used, thus avoiding clashes and keeping images size down. Fixes #1081 Signed-off-by: Luca Boccassi <luca.boccassi@microsoft.com>
This commit is contained in:
parent
2b41c409e7
commit
488fd4c3df
|
@ -151,9 +151,16 @@ edit_cmd += -e 's|@LIBSELINUX[@]||g'
|
|||
endif
|
||||
|
||||
if HAVE_CRYPTSETUP
|
||||
if CRYPTSETUP_VIA_DLOPEN
|
||||
edit_cmd += -e 's|@LIBCRYPTSETUP[@]||g'
|
||||
edit_cmd += -e 's|@LIBDL[@]|-ldl|g'
|
||||
else
|
||||
edit_cmd += -e 's|@LIBCRYPTSETUP[@]|libcryptsetup|g'
|
||||
edit_cmd += -e 's|@LIBDL[@]||g'
|
||||
endif
|
||||
else
|
||||
edit_cmd += -e 's|@LIBCRYPTSETUP[@]||g'
|
||||
edit_cmd += -e 's|@LIBDL[@]||g'
|
||||
endif
|
||||
|
||||
if USE_VENDORDIR
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -2516,6 +2516,7 @@ AC_ARG_WITH([cryptsetup],
|
|||
|
||||
AS_IF([test "x$with_cryptsetup" = xno], [
|
||||
AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])
|
||||
AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
|
||||
], [
|
||||
PKG_CHECK_MODULES([CRYPTSETUP], [libcryptsetup],
|
||||
[AC_DEFINE([HAVE_CRYPTSETUP], [1], [Define if cryptsetup is available])
|
||||
|
@ -2528,11 +2529,22 @@ AS_IF([test "x$with_cryptsetup" = xno], [
|
|||
AC_CHECK_LIB([cryptsetup], [crypt_activate_by_signed_key], [
|
||||
AC_DEFINE_UNQUOTED([HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY], [1], [Define if crypt_activate_by_signed_key exist in -lcryptsetup])
|
||||
])
|
||||
AS_IF([test "x$with_cryptsetup" = xdlopen], [
|
||||
LIBS="-ldl $LIBS"
|
||||
AC_CHECK_LIB([dl], [dlsym], [
|
||||
AC_DEFINE([CRYPTSETUP_VIA_DLOPEN], [1], [Define if cryptsetup is to be loaded via dlopen])
|
||||
AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [true])
|
||||
], [AC_MSG_ERROR([libdl required to build with cryptsetup support])])
|
||||
], [
|
||||
AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
|
||||
])
|
||||
CFLAGS="$SAVE_CFLAGS"
|
||||
LIBS="$SAVE_LIBS"
|
||||
have_cryptsetup=yes],
|
||||
[have_cryptsetup=no
|
||||
AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])])
|
||||
AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])
|
||||
AM_CONDITIONAL([CRYPTSETUP_VIA_DLOPEN], [false])
|
||||
])
|
||||
|
||||
AS_CASE([$with_cryptsetup:$have_cryptsetup],
|
||||
[yes:no], [AC_MSG_WARN([cryptsetup selected but libcryptsetup not found])]
|
||||
|
|
|
@ -20,3 +20,4 @@ Version: @LIBMOUNT_VERSION@
|
|||
Requires.private: blkid @LIBSELINUX@ @LIBCRYPTSETUP@
|
||||
Cflags: -I${includedir}/libmount
|
||||
Libs: -L${libdir} -lmount
|
||||
Libs.private: @LIBDL@
|
||||
|
|
|
@ -44,9 +44,16 @@ libmount_la_LIBADD = \
|
|||
libcommon.la \
|
||||
libblkid.la \
|
||||
$(SELINUX_LIBS) \
|
||||
$(CRYPTSETUP_LIBS) \
|
||||
$(REALTIME_LIBS)
|
||||
|
||||
if HAVE_CRYPTSETUP
|
||||
if CRYPTSETUP_VIA_DLOPEN
|
||||
libmount_la_LIBADD += -ldl
|
||||
else
|
||||
libmount_la_LIBADD += $(CRYPTSETUP_LIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
libmount_la_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
$(SOLIB_CFLAGS) \
|
||||
|
|
|
@ -14,9 +14,29 @@
|
|||
|
||||
#if defined(HAVE_CRYPTSETUP)
|
||||
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <libcryptsetup.h>
|
||||
#include "path.h"
|
||||
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
static void *get_symbol(struct libmnt_context *cxt, void *dl, const char *name, int *rc)
|
||||
{
|
||||
char *dl_error = NULL;
|
||||
void *sym = dlsym(dl, name);
|
||||
|
||||
*rc = 0;
|
||||
if ((dl_error = dlerror()) == NULL)
|
||||
return sym;
|
||||
|
||||
DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected but cannot dlopen symbol %s: %s", name, dl_error));
|
||||
*rc = -ENOTSUP;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Taken from https://gitlab.com/cryptsetup/cryptsetup/blob/master/lib/utils_crypt.c#L225 */
|
||||
static size_t crypt_hex_to_bytes(const char *hex, char **result)
|
||||
{
|
||||
|
@ -58,6 +78,33 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
/* Use the same default for FEC parity bytes as cryptsetup uses */
|
||||
uint64_t offset = 0, fec_offset = 0, fec_roots = 2;
|
||||
struct stat hash_sig_st;
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
/* To avoid linking libmount to libcryptsetup, and keep the default dependencies list down, use dlopen */
|
||||
void *dl = NULL;
|
||||
int (*sym_crypt_init_data_device)(struct crypt_device **, const char *, const char *) = NULL;
|
||||
int (*sym_crypt_load)(struct crypt_device *, const char *, void *) = NULL;
|
||||
int (*sym_crypt_get_volume_key_size)(struct crypt_device *) = NULL;
|
||||
#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
|
||||
int (*sym_crypt_activate_by_signed_key)(struct crypt_device *, const char *, const char *, size_t, const char *, size_t, uint32_t) = NULL;
|
||||
#endif
|
||||
int (*sym_crypt_activate_by_volume_key)(struct crypt_device *, const char *, const char *, size_t, uint32_t) = NULL;
|
||||
void (*sym_crypt_free)(struct crypt_device *) = NULL;
|
||||
int (*sym_crypt_init_by_name)(struct crypt_device **, const char *) = NULL;
|
||||
int (*sym_crypt_get_verity_info)(struct crypt_device *, struct crypt_params_verity *) = NULL;
|
||||
int (*sym_crypt_volume_key_get)(struct crypt_device *, int, char *, size_t *, const char *, size_t) = NULL;
|
||||
#else
|
||||
int (*sym_crypt_init_data_device)(struct crypt_device **, const char *, const char *) = &crypt_init_data_device;
|
||||
int (*sym_crypt_load)(struct crypt_device *, const char *, void *) = &crypt_load;
|
||||
int (*sym_crypt_get_volume_key_size)(struct crypt_device *) = &crypt_get_volume_key_size;
|
||||
#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
|
||||
int (*sym_crypt_activate_by_signed_key)(struct crypt_device *, const char *, const char *, size_t, const char *, size_t, uint32_t) = &crypt_activate_by_signed_key;
|
||||
#endif
|
||||
int (*sym_crypt_activate_by_volume_key)(struct crypt_device *, const char *, const char *, size_t, uint32_t) = &crypt_activate_by_volume_key;
|
||||
void (*sym_crypt_free)(struct crypt_device *) = &crypt_free;
|
||||
int (*sym_crypt_init_by_name)(struct crypt_device **, const char *) = &crypt_init_by_name;
|
||||
int (*sym_crypt_get_verity_info)(struct crypt_device *, struct crypt_params_verity *) = &crypt_get_verity_info;
|
||||
int (*sym_crypt_volume_key_get)(struct crypt_device *, int, char *, size_t *, const char *, size_t) = &crypt_volume_key_get;
|
||||
#endif
|
||||
|
||||
assert(cxt);
|
||||
assert(cxt->fs);
|
||||
|
@ -186,10 +233,61 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
if (rc == 0) {
|
||||
int dl_flags = RTLD_LAZY | RTLD_LOCAL;
|
||||
/* glibc extension: mnt_context_deferred_delete_veritydev is called immediately after, don't unload on dl_close */
|
||||
#ifdef RTLD_NODELETE
|
||||
dl_flags |= RTLD_NODELETE;
|
||||
#endif
|
||||
/* glibc extension: might help to avoid further symbols clashes */
|
||||
#ifdef RTLD_DEEPBIND
|
||||
dl_flags |= RTLD_DEEPBIND;
|
||||
#endif
|
||||
dl = dlopen("libcryptsetup.so.12", dl_flags);
|
||||
if (!dl) {
|
||||
DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected but cannot dlopen libcryptsetup"));
|
||||
rc = -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* clear errors first, then load all the libcryptsetup symbols */
|
||||
dlerror();
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_init_data_device) = get_symbol(cxt, dl, "crypt_init_data_device", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_load) = get_symbol(cxt, dl, "crypt_load", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_get_volume_key_size) = get_symbol(cxt, dl, "crypt_get_volume_key_size", &rc);
|
||||
|
||||
#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_activate_by_signed_key) = get_symbol(cxt, dl, "crypt_activate_by_signed_key", &rc);
|
||||
#endif
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_activate_by_volume_key) = get_symbol(cxt, dl, "crypt_activate_by_volume_key", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_free) = get_symbol(cxt, dl, "crypt_free", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_init_by_name) = get_symbol(cxt, dl, "crypt_init_by_name", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_get_verity_info) = get_symbol(cxt, dl, "crypt_get_verity_info", &rc);
|
||||
|
||||
if (rc == 0)
|
||||
*(void **)(&sym_crypt_volume_key_get) = get_symbol(cxt, dl, "crypt_volume_key_get", &rc);
|
||||
#endif
|
||||
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
rc = crypt_init_data_device(&crypt_dev, hash_device, backing_file);
|
||||
rc = (*sym_crypt_init_data_device)(&crypt_dev, hash_device, backing_file);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
|
@ -199,11 +297,11 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
crypt_params.fec_roots = fec_roots;
|
||||
crypt_params.fec_device = fec_device;
|
||||
crypt_params.flags = 0;
|
||||
rc = crypt_load(crypt_dev, CRYPT_VERITY, &crypt_params);
|
||||
rc = (*sym_crypt_load)(crypt_dev, CRYPT_VERITY, &crypt_params);
|
||||
if (rc < 0)
|
||||
goto done;
|
||||
|
||||
hash_size = crypt_get_volume_key_size(crypt_dev);
|
||||
hash_size = (*sym_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));
|
||||
rc = -EINVAL;
|
||||
|
@ -211,14 +309,14 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
}
|
||||
if (hash_sig) {
|
||||
#ifdef HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
|
||||
rc = crypt_activate_by_signed_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
|
||||
rc = (*sym_crypt_activate_by_signed_key)(crypt_dev, mapper_device, root_hash_binary, hash_size,
|
||||
hash_sig, hash_sig_size, CRYPT_ACTIVATE_READONLY);
|
||||
#else
|
||||
rc = -EINVAL;
|
||||
DBG(VERITY, ul_debugobj(cxt, "verity.roothashsig=%s passed but libcryptsetup does not provide crypt_activate_by_signed_key()", hash_sig));
|
||||
#endif
|
||||
} else
|
||||
rc = crypt_activate_by_volume_key(crypt_dev, mapper_device, root_hash_binary, hash_size,
|
||||
rc = (*sym_crypt_activate_by_volume_key)(crypt_dev, mapper_device, root_hash_binary, hash_size,
|
||||
CRYPT_ACTIVATE_READONLY);
|
||||
/*
|
||||
* If the mapper device already exists, and if libcryptsetup supports it, get the root
|
||||
|
@ -232,10 +330,10 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
*/
|
||||
if (rc == -EEXIST) {
|
||||
DBG(VERITY, ul_debugobj(cxt, "%s already in use as /dev/mapper/%s", backing_file, mapper_device));
|
||||
crypt_free(crypt_dev);
|
||||
rc = crypt_init_by_name(&crypt_dev, mapper_device);
|
||||
(*sym_crypt_free)(crypt_dev);
|
||||
rc = (*sym_crypt_init_by_name)(&crypt_dev, mapper_device);
|
||||
if (!rc) {
|
||||
rc = crypt_get_verity_info(crypt_dev, &crypt_params);
|
||||
rc = (*sym_crypt_get_verity_info)(crypt_dev, &crypt_params);
|
||||
if (!rc) {
|
||||
key = calloc(hash_size, 1);
|
||||
if (!key) {
|
||||
|
@ -245,7 +343,7 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
}
|
||||
if (!rc) {
|
||||
keysize = hash_size;
|
||||
rc = crypt_volume_key_get(crypt_dev, CRYPT_ANY_SLOT, key, &keysize, NULL, 0);
|
||||
rc = (*sym_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));
|
||||
|
@ -290,7 +388,12 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt)
|
|||
}
|
||||
|
||||
done:
|
||||
crypt_free(crypt_dev);
|
||||
if (sym_crypt_free)
|
||||
(*sym_crypt_free)(crypt_dev);
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
if (dl)
|
||||
dlclose(dl);
|
||||
#endif
|
||||
free(root_hash_binary);
|
||||
free(mapper_device_full);
|
||||
free(mapper_device);
|
||||
|
@ -309,7 +412,22 @@ int mnt_context_deferred_delete_veritydev(struct libmnt_context *cxt)
|
|||
struct crypt_device *crypt_dev = NULL;
|
||||
/* If mounting failed delete immediately, otherwise setup auto cleanup for user umount */
|
||||
uint32_t flags = mnt_context_get_status(cxt) ? CRYPT_DEACTIVATE_DEFERRED : 0;
|
||||
int rc;
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
void *dl = NULL;
|
||||
int dl_flags = RTLD_LAZY | RTLD_LOCAL;
|
||||
/* glibc extension: might help to avoid further symbols clashes */
|
||||
#ifdef RTLD_DEEPBIND
|
||||
dl_flags |= RTLD_DEEPBIND;
|
||||
#endif
|
||||
int (*sym_crypt_init_by_name)(struct crypt_device **, const char *) = NULL;
|
||||
int (*sym_crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t) = NULL;
|
||||
void (*sym_crypt_free)(struct crypt_device *) = NULL;
|
||||
#else
|
||||
int (*sym_crypt_init_by_name)(struct crypt_device **, const char *) = &crypt_init_by_name;
|
||||
int (*sym_crypt_deactivate_by_name)(struct crypt_device *, const char *, uint32_t) = &crypt_deactivate_by_name;
|
||||
void (*sym_crypt_free)(struct crypt_device *) = &crypt_free;
|
||||
#endif
|
||||
int rc = 0;
|
||||
|
||||
assert(cxt);
|
||||
assert(cxt->fs);
|
||||
|
@ -321,14 +439,38 @@ int mnt_context_deferred_delete_veritydev(struct libmnt_context *cxt)
|
|||
if (!src)
|
||||
return -EINVAL;
|
||||
|
||||
rc = crypt_init_by_name(&crypt_dev, src);
|
||||
if (!rc) {
|
||||
rc = crypt_deactivate_by_name(crypt_dev, src, flags);
|
||||
if (!rc)
|
||||
cxt->flags &= ~MNT_FL_VERITYDEV_READY;
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
dl = dlopen("libcryptsetup.so.12", dl_flags);
|
||||
if (!dl) {
|
||||
DBG(VERITY, ul_debugobj(cxt, "veritydev specific options detected but cannot dlopen libcryptsetup"));
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
crypt_free(crypt_dev);
|
||||
/* clear errors first */
|
||||
dlerror();
|
||||
|
||||
if (!rc)
|
||||
*(void **)(&sym_crypt_init_by_name) = get_symbol(cxt, dl, "crypt_init_by_name", &rc);
|
||||
if (!rc)
|
||||
*(void **)(&sym_crypt_deactivate_by_name) = get_symbol(cxt, dl, "crypt_deactivate_by_name", &rc);
|
||||
if (!rc)
|
||||
*(void **)(&sym_crypt_free) = get_symbol(cxt, dl, "crypt_free", &rc);
|
||||
#endif
|
||||
|
||||
if (!rc) {
|
||||
rc = (*sym_crypt_init_by_name)(&crypt_dev, src);
|
||||
if (!rc) {
|
||||
rc = (*sym_crypt_deactivate_by_name)(crypt_dev, src, flags);
|
||||
if (!rc)
|
||||
cxt->flags &= ~MNT_FL_VERITYDEV_READY;
|
||||
}
|
||||
|
||||
(*sym_crypt_free)(crypt_dev);
|
||||
}
|
||||
|
||||
#ifdef CRYPTSETUP_VIA_DLOPEN
|
||||
dlclose(dl);
|
||||
#endif
|
||||
|
||||
DBG(VERITY, ul_debugobj(cxt, "deleted [rc=%d]", rc));
|
||||
|
||||
|
|
|
@ -2554,8 +2554,8 @@ checking of block devices using kernel crypto API. The
|
|||
.B mount
|
||||
command can open
|
||||
the dm-verity device and do the integrity verification before on the device
|
||||
filesystem is mounted. Requires libcryptsetup with in libmount. If
|
||||
libcryptsetup supports extracting the root hash of an already mounted device,
|
||||
filesystem is mounted. Requires libcryptsetup with in libmount (optionally via dlopen).
|
||||
If libcryptsetup supports extracting the root hash of an already mounted device,
|
||||
existing devices will be automatically reused in case of a match.
|
||||
Mount options for dm-verity:
|
||||
.TP
|
||||
|
|
Loading…
Reference in New Issue