]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libmount: add support for signed verity devices
authorLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 4 Dec 2019 15:06:55 +0000 (15:06 +0000)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Mon, 13 Jan 2020 12:24:55 +0000 (12:24 +0000)
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
libmount/docs/libmount-sections.txt
libmount/python/pylibmount.c
libmount/src/context_veritydev.c
libmount/src/libmount.h.in
libmount/src/optmap.c
sys-utils/mount.8

index e649c9c2013fe3dbca4a0b6d3e9a230853584e50..75a7520725cd468696317597d537d35373bbf963 100644 (file)
@@ -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])])
index baba1093f58ba5219dc8e64802296e35c8a15f83..da96b75b323c67aebba5f4ec5f9e3e058715f6ea 100644 (file)
@@ -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
 <SUBSECTION>
 MS_BIND
 MS_DIRSYNC
index b7c39e502b8f69288bff28091b03dc1da128f3b2..b0b26057817ed641cf57861a778dca89724ab4c7 100644 (file)
@@ -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)
index fd5662ce56207b26b174e23a7b9660166f770b61..47290479c597758352f51c78e1fd4c851a05d267 100644 (file)
@@ -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;
 }
index e686b0fc737b6b0c00d640bbf5c17460d8ed1fb7..321c0540bb9c05111959c1b36881108feffcc082 100644 (file)
@@ -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)
index 63f4f2564fb3c11aaed707123774232b04e81c6e..abd81bc8eec8714749cc040d9ad538bdbc801c0f 100644 (file)
@@ -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 }
 };
index 162e2387acca8cfc97bbd0c1553f23eab050d327..3e57251ed9f9bce1b6249290ee4390cc374eba46 100644 (file)
@@ -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=<hash> /tmp/etc.squashfs /mnt
+.B openssl smime \-sign \-in <hash> \-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=<hash>,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,