From 123b1a6708c37e84c939d63e9f380b81ad1146d3 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 4 Dec 2019 15:06:55 +0000 Subject: [PATCH] libmount: add support for signed verity devices A new API was added to libcryptsetup to make use of the kernel's new CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG feature, which allows to sign root hashes. Add a verity.roothashsig option to use it. Device reuse will be allowed only if signatures are used by all, or by none. --- configure.ac | 9 ++++++ libmount/docs/libmount-sections.txt | 1 + libmount/python/pylibmount.c | 1 + libmount/src/context_veritydev.c | 48 +++++++++++++++++++++++++++-- libmount/src/libmount.h.in | 1 + libmount/src/optmap.c | 1 + sys-utils/mount.8 | 10 +++++- 7 files changed, 67 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index e649c9c201..75a7520725 100644 --- a/configure.ac +++ b/configure.ac @@ -2443,6 +2443,15 @@ AS_IF([test "x$with_cryptsetup" = xno], [ [AC_DEFINE([HAVE_CRYPTSETUP], [1], [Define if cryptsetup is available]) UL_PKG_STATIC([CRYPTSETUP_LIBS_STATIC], [libcryptsetup]) AM_CONDITIONAL([HAVE_CRYPTSETUP], [true]) + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CRYPTSETUP_CFLAGS $CFLAGS" + SAVE_LIBS="$LIBS" + LIBS="$CRYPTSETUP_LIBS $LIBS" + 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]) + ]) + CFLAGS="$SAVE_CFLAGS" + LIBS="$SAVE_LIBS" have_cryptsetup=yes], [have_cryptsetup=no AM_CONDITIONAL([HAVE_CRYPTSETUP], [false])]) diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index baba1093f5..da96b75b32 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -164,6 +164,7 @@ MNT_MS_ROOT_HASH_FILE MNT_MS_FEC_DEVICE MNT_MS_FEC_OFFSET MNT_MS_FEC_ROOTS +MNT_MS_ROOT_HASH_SIG MS_BIND MS_DIRSYNC diff --git a/libmount/python/pylibmount.c b/libmount/python/pylibmount.c index b7c39e502b..b0b2605781 100644 --- a/libmount/python/pylibmount.c +++ b/libmount/python/pylibmount.c @@ -258,6 +258,7 @@ PyMODINIT_FUNC initpylibmount(void) PyModule_AddIntConstant(m, "MNT_MS_FEC_DEVICE", MNT_MS_FEC_DEVICE); PyModule_AddIntConstant(m, "MNT_MS_FEC_OFFSET", MNT_MS_FEC_OFFSET); PyModule_AddIntConstant(m, "MNT_MS_FEC_ROOTS", MNT_MS_FEC_ROOTS); + PyModule_AddIntConstant(m, "MNT_MS_ROOT_HASH_SIG", MNT_MS_ROOT_HASH_SIG); /* * mount(2) MS_* masks (MNT_MAP_LINUX map) diff --git a/libmount/src/context_veritydev.c b/libmount/src/context_veritydev.c index fd5662ce56..47290479c5 100644 --- a/libmount/src/context_veritydev.c +++ b/libmount/src/context_veritydev.c @@ -50,13 +50,14 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) const char *backing_file, *optstr; char *val = NULL, *key = NULL, *root_hash_binary = NULL, *mapper_device = NULL, *mapper_device_full = NULL, *backing_file_basename = NULL, *root_hash = NULL, - *hash_device = NULL, *root_hash_file = NULL, *fec_device = NULL; - size_t len, hash_size, keysize = 0; + *hash_device = NULL, *root_hash_file = NULL, *fec_device = NULL, *hash_sig = NULL; + size_t len, 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; + struct stat hash_sig_st; assert(cxt); assert(cxt->fs); @@ -153,6 +154,25 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) } } + /* + * verity.roothashsig= + */ + if (rc == 0 && (cxt->user_mountflags & MNT_MS_ROOT_HASH_SIG) && + mnt_optstr_get_option(optstr, "verity.roothashsig", &val, &len) == 0 && val) { + rc = ul_path_stat(NULL, &hash_sig_st, val); + if (rc == 0) + rc = !S_ISREG(hash_sig_st.st_mode) || !hash_sig_st.st_size ? -EINVAL : 0; + 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, val); + rc = rc < (int)hash_sig_size ? -1 : 0; + } + } + if (!rc && root_hash && root_hash_file) { DBG(VERITY, ul_debugobj(cxt, "verity.roothash and verity.roothashfile are mutually exclusive")); rc = -EINVAL; @@ -189,7 +209,16 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) rc = -EINVAL; goto done; } - rc = crypt_activate_by_volume_key(crypt_dev, mapper_device, root_hash_binary, hash_size, + 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, + 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, CRYPT_ACTIVATE_READONLY); /* * If the mapper device already exists, and if libcryptsetup supports it, get the root @@ -232,6 +261,18 @@ int mnt_context_setup_veritydev(struct libmnt_context *cxt) if (rc) { rc = -EEXIST; } else { + /* + * Ensure that, if signatures are supported, we only reuse the device if the previous mount + * used the same settings, so that a previous unsigned mount will not be reused if the user + * asks to use signing for the new one, and viceversa. + */ +#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")); + goto done; + } +#endif DBG(VERITY, ul_debugobj(cxt, "root hash of %s matches %s, reusing device", mapper_device, root_hash)); } } @@ -257,6 +298,7 @@ done: free(root_hash); free(root_hash_file); free(fec_device); + free(hash_sig); free(key); return rc; } diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index e686b0fc73..321c0540bb 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -912,6 +912,7 @@ extern int mnt_context_set_syscall_status(struct libmnt_context *cxt, int status #define MNT_MS_FEC_DEVICE (1 << 22) #define MNT_MS_FEC_OFFSET (1 << 23) #define MNT_MS_FEC_ROOTS (1 << 24) +#define MNT_MS_ROOT_HASH_SIG (1 << 25) /* * mount(2) MS_* masks (MNT_MAP_LINUX map) diff --git a/libmount/src/optmap.c b/libmount/src/optmap.c index 63f4f2564f..abd81bc8ee 100644 --- a/libmount/src/optmap.c +++ b/libmount/src/optmap.c @@ -186,6 +186,7 @@ static const struct libmnt_optmap userspace_opts_map[] = { "verity.fecdevice=", MNT_MS_FEC_DEVICE, MNT_NOHLPS | MNT_NOMTAB }, /* verity FEC device */ { "verity.fecoffset=", MNT_MS_FEC_OFFSET, MNT_NOHLPS | MNT_NOMTAB }, /* verity FEC area offset */ { "verity.fecroots=", MNT_MS_FEC_ROOTS, MNT_NOHLPS | MNT_NOMTAB }, /* verity FEC roots */ + { "verity.roothashsig=", MNT_MS_ROOT_HASH_SIG, MNT_NOHLPS | MNT_NOMTAB }, /* verity device root hash signature file */ { NULL, 0, 0 } }; diff --git a/sys-utils/mount.8 b/sys-utils/mount.8 index 162e2387ac..3e57251ed9 100644 --- a/sys-utils/mount.8 +++ b/sys-utils/mount.8 @@ -2414,6 +2414,12 @@ If the FEC device is embedded in the source volume, .TP \fBverity.fecroots=\fP\,\fIvalue\fP Parity bytes for FEC (default: 2). Optional. +.TP +\fBverity.roothashsig=\fP\,\fIpath\fP +Path to pkcs7 signature of root hash hex string. Requires crypt_activate_by_signed_key() from cryptsetup and +kernel built with CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG. For device reuse, signatures have to be either used by all +mounts of a device or by none. Optional. +.RE .PP Supported since util-linux v2.35. .PP @@ -2424,12 +2430,14 @@ For example commands: .B mksquashfs /etc /tmp/etc.squashfs .B dd if=/dev/zero of=/tmp/etc.hash bs=1M count=10 .B veritysetup format /tmp/etc.squashfs /tmp/etc.hash -.B mount \-o verity.hashdevice=/tmp/etc.hash,verity.roothash= /tmp/etc.squashfs /mnt +.B openssl smime \-sign \-in \-nocerts \-inkey private.key \-signer private.crt \-noattr \-binary \-outform der \-out /tmp/etc.p7 +.B mount \-o verity.hashdevice=/tmp/etc.hash,verity.roothash=,verity.roothashsig=/tmp/etc.p7 /tmp/etc.squashfs /mnt .fi .RE .sp create squashfs image from /etc directory, verity hash device and mount verified filesystem image to /mnt. +The kernel will verify that the root hash is signed by a key from the kernel keyring if roothashsig is used. .SH "LOOP-DEVICE SUPPORT" One further possible type is a mount via the loop device. For example, -- 2.47.2