Re: [PATCH v5 01/16] fs-verity: add a documentation file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 06/20, Eric Biggers wrote:
> From: Eric Biggers <ebiggers@xxxxxxxxxx>
> 
> Add a documentation file for fs-verity, covering:
> 
> - Introduction
> - Use cases
> - User API
>     - FS_IOC_ENABLE_VERITY
>     - FS_IOC_MEASURE_VERITY
>     - FS_IOC_GETFLAGS
> - Accessing verity files
> - File measurement computation
>     - Merkle tree
>     - fs-verity descriptor
> - Built-in signature verification
> - Filesystem support
>     - ext4
>     - f2fs
> - Implementation details
>     - Verifying data
>         - Pagecache
>         - Block device based filesystems
> - Userspace utility
> - Tests
> - FAQ
> 
> Reviewed-by: Theodore Ts'o <tytso@xxxxxxx>

Reviewed-by: Jaegeuk Kim <jaegeuk@xxxxxxxxxx>

> Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>
> ---
>  Documentation/filesystems/fsverity.rst | 710 +++++++++++++++++++++++++
>  Documentation/filesystems/index.rst    |   1 +
>  2 files changed, 711 insertions(+)
>  create mode 100644 Documentation/filesystems/fsverity.rst
> 
> diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst
> new file mode 100644
> index 00000000000000..49524d7ea190e5
> --- /dev/null
> +++ b/Documentation/filesystems/fsverity.rst
> @@ -0,0 +1,710 @@
> +=======================================================
> +fs-verity: read-only file-based authenticity protection
> +=======================================================
> +
> +Introduction
> +============
> +
> +fs-verity (``fs/verity/``) is a support layer that filesystems can
> +hook into to support transparent integrity and authenticity protection
> +of read-only files.  Currently, it is supported by the ext4 and f2fs
> +filesystems.  Like fscrypt, not too much filesystem-specific code is
> +needed to support fs-verity.
> +
> +fs-verity is similar to `dm-verity
> +<https://www.kernel.org/doc/Documentation/device-mapper/verity.txt>`_
> +but works on files rather than block devices.  On regular files on
> +filesystems supporting fs-verity, userspace can execute an ioctl that
> +causes the filesystem to build a Merkle tree for the file and persist
> +it to a filesystem-specific location associated with the file.
> +
> +After this, the file is made readonly, and all reads from the file are
> +automatically verified against the file's Merkle tree.  Reads of any
> +corrupted data, including mmap reads, will fail.
> +
> +Userspace can use another ioctl to retrieve the root hash (actually
> +the "file measurement", which is a hash that includes the root hash)
> +that fs-verity is enforcing for the file.  This ioctl executes in
> +constant time, regardless of the file size.
> +
> +fs-verity is essentially a way to hash a file in constant time,
> +subject to the caveat that reads which would violate the hash will
> +fail at runtime.
> +
> +Use cases
> +=========
> +
> +By itself, the base fs-verity feature only provides integrity
> +protection, i.e. detection of accidental (non-malicious) corruption.
> +
> +However, because fs-verity makes retrieving the file hash extremely
> +efficient, it's primarily meant to be used as a tool to support
> +authentication (detection of malicious modifications) or auditing
> +(logging file hashes before use).
> +
> +Trusted userspace code (e.g. operating system code running on a
> +read-only partition that is itself authenticated by dm-verity) can
> +authenticate the contents of an fs-verity file by using the
> +`FS_IOC_MEASURE_VERITY`_ ioctl to retrieve its hash, then verifying a
> +digital signature of it.
> +
> +A standard file hash could be used instead of fs-verity.  However,
> +this is inefficient if the file is large and only a small portion may
> +be accessed.  This is often the case for Android application package
> +(APK) files, for example.  These typically contain many translations,
> +classes, and other resources that are infrequently or even never
> +accessed on a particular device.  It would be slow and wasteful to
> +read and hash the entire file before starting the application.
> +
> +Unlike an ahead-of-time hash, fs-verity also re-verifies data each
> +time it's paged in.  This ensures that malicious disk firmware can't
> +undetectably change the contents of the file at runtime.
> +
> +fs-verity does not replace or obsolete dm-verity.  dm-verity should
> +still be used on read-only filesystems.  fs-verity is for files that
> +must live on a read-write filesystem because they are independently
> +updated and potentially user-installed, so dm-verity cannot be used.
> +
> +The base fs-verity feature is a hashing mechanism only; actually
> +authenticating the files is up to userspace.  However, to meet some
> +users' needs, fs-verity optionally supports a simple signature
> +verification mechanism where users can configure the kernel to require
> +that all fs-verity files be signed by a key loaded into a keyring; see
> +`Built-in signature verification`_.  Support for fs-verity file hashes
> +in IMA (Integrity Measurement Architecture) policies is also planned.
> +
> +User API
> +========
> +
> +FS_IOC_ENABLE_VERITY
> +--------------------
> +
> +The FS_IOC_ENABLE_VERITY ioctl enables fs-verity on a file.  It takes
> +in a pointer to a :c:type:`struct fsverity_enable_arg`, defined as
> +follows::
> +
> +    struct fsverity_enable_arg {
> +            __u32 version;
> +            __u32 hash_algorithm;
> +            __u32 block_size;
> +            __u32 salt_size;
> +            __u64 salt_ptr;
> +            __u32 sig_size;
> +            __u32 __reserved1;
> +            __u64 sig_ptr;
> +            __u64 __reserved2[11];
> +    };
> +
> +This structure contains the parameters of the Merkle tree to build for
> +the file, and optionally contains a signature.  It must be initialized
> +as follows:
> +
> +- ``version`` must be 1.
> +- ``hash_algorithm`` must be the identifier for the hash algorithm to
> +  use for the Merkle tree, such as FS_VERITY_HASH_ALG_SHA256.  See
> +  ``include/uapi/linux/fsverity.h`` for the list of possible values.
> +- ``block_size`` must be the Merkle tree block size.  Currently, this
> +  must be equal to the system page size, which is usually 4096 bytes.
> +  Other sizes may be supported in the future.  This value is not
> +  necessarily the same as the filesystem block size.
> +- ``salt_size`` is the size of the salt in bytes, or 0 if no salt is
> +  provided.  The salt is a value that is prepended to every hashed
> +  block; it can be used to personalize the hashing for a particular
> +  file or device.  Currently the maximum salt size is 32 bytes.
> +- ``salt_ptr`` is the pointer to the salt, or NULL if no salt is
> +  provided.
> +- ``sig_size`` is the size of the signature in bytes, or 0 if no
> +  signature is provided.  Currently the signature is (somewhat
> +  arbitrarily) limited to 16128 bytes.  See `Built-in signature
> +  verification`_ for more information.
> +- ``sig_ptr``  is the pointer to the signature, or NULL if no
> +  signature is provided.
> +- All reserved fields must be zeroed.
> +
> +FS_IOC_ENABLE_VERITY causes the filesystem to build a Merkle tree for
> +the file and persist it to a filesystem-specific location associated
> +with the file, then mark the file as a verity file.  This ioctl may
> +take a long time to execute on large files, and it is interruptible by
> +fatal signals.
> +
> +FS_IOC_ENABLE_VERITY checks for write access to the inode.  However,
> +it must be executed on an O_RDONLY file descriptor and no processes
> +can have the file open for writing.  Attempts to open the file for
> +writing while this ioctl is executing will fail with ETXTBSY.  (This
> +is necessary to guarantee that no writable file descriptors will exist
> +after verity is enabled, and to guarantee that the file's contents are
> +stable while the Merkle tree is being built over it.)
> +
> +On success, FS_IOC_ENABLE_VERITY returns 0, and the file becomes a
> +verity file.  On failure (including the case of interruption by a
> +fatal signal), no changes are made to the file.
> +
> +FS_IOC_ENABLE_VERITY can fail with the following errors:
> +
> +- ``EACCES``: the process does not have write access to the file
> +- ``EEXIST``: the file already has verity enabled
> +- ``EFAULT``: the caller provided inaccessible memory
> +- ``EINTR``: the operation was interrupted by a fatal signal
> +- ``EINVAL``: unsupported version, hash algorithm, or block size; or
> +  reserved bits are set; or the file descriptor refers to neither a
> +  regular file nor a directory.
> +- ``EISDIR``: the file descriptor refers to a directory
> +- ``EMSGSIZE``: the salt or signature is too long
> +- ``ENOENT``: fs-verity recognizes the hash algorithm, but it's not
> +  available in the kernel's crypto API as currently configured (e.g.
> +  for SHA-512, missing CONFIG_CRYPTO_SHA512).
> +- ``ENOTTY``: this type of filesystem does not implement fs-verity
> +- ``EOPNOTSUPP``: the kernel was not configured with fs-verity
> +  support; or the filesystem superblock has not had the 'verity'
> +  feature enabled on it; or the filesystem does not support fs-verity
> +  on this file.  (See `Filesystem support`_.)
> +- ``EPERM``: the file is append-only
> +- ``EROFS``: the filesystem is read-only
> +- ``ETXTBSY``: someone has the file open for writing.  This can be the
> +  caller's file descriptor, another open file descriptor, or the file
> +  reference held by a writable memory map.
> +
> +FS_IOC_MEASURE_VERITY
> +---------------------
> +
> +The FS_IOC_MEASURE_VERITY ioctl retrieves the measurement of a verity
> +file.  The file measurement is a digest that cryptographically
> +identifies the file contents that are being enforced on reads.
> +
> +This ioctl takes in a pointer to a variable-length structure::
> +
> +    struct fsverity_digest {
> +            __u16 digest_algorithm;
> +            __u16 digest_size; /* input/output */
> +            __u8 digest[];
> +    };
> +
> +``digest_size`` is an input/output field.  On input, it must be
> +initialized to the number of bytes allocated for the variable-length
> +``digest`` field.
> +
> +On success, 0 is returned and the kernel fills in the structure as
> +follows:
> +
> +- ``digest_algorithm`` will be the hash algorithm used for the file
> +  measurement.  It will match ``fsverity_enable_arg::hash_algorithm``.
> +- ``digest_size`` will be the size of the digest in bytes, e.g. 32
> +  for SHA-256.  (This can be redundant with ``digest_algorithm``.)
> +- ``digest`` will be the actual bytes of the digest.
> +
> +FS_IOC_MEASURE_VERITY is guaranteed to execute in constant time,
> +regardless of the size of the file.
> +
> +FS_IOC_MEASURE_VERITY can fail with the following errors:
> +
> +- ``EFAULT``: the caller provided inaccessible memory
> +- ``ENODATA``: the file is not a verity file
> +- ``ENOTTY``: this type of filesystem does not implement fs-verity
> +- ``EOPNOTSUPP``: the kernel was not configured with fs-verity
> +  support, or the filesystem superblock has not had the 'verity'
> +  feature enabled on it.  (See `Filesystem support`_.)
> +- ``EOVERFLOW``: the digest is longer than the specified
> +  ``digest_size`` bytes.  Try providing a larger buffer.
> +
> +FS_IOC_GETFLAGS
> +---------------
> +
> +The existing ioctl FS_IOC_GETFLAGS (which isn't specific to fs-verity)
> +can also be used to check whether a file has fs-verity enabled or not.
> +To do so, check for FS_VERITY_FL (0x00100000) in the returned flags.
> +
> +The verity flag is not settable via FS_IOC_SETFLAGS.  You must use
> +FS_IOC_ENABLE_VERITY instead, since parameters must be provided.
> +
> +Accessing verity files
> +======================
> +
> +Applications can transparently access a verity file just like a
> +non-verity one, with the following exceptions:
> +
> +- Verity files are readonly.  They cannot be opened for writing or
> +  truncate()d, even if the file mode bits allow it.  Attempts to do
> +  one of these things will fail with EPERM.  However, changes to
> +  metadata such as owner, mode, timestamps, and xattrs are still
> +  allowed, since these are not measured by fs-verity.  Verity files
> +  can also still be renamed, deleted, and linked to.
> +
> +- Direct I/O is not supported on verity files.  Attempts to use direct
> +  I/O on such files will fall back to buffered I/O.
> +
> +- DAX (Direct Access) is not supported on verity files, because this
> +  would circumvent the data verification.
> +
> +- Reads of data that doesn't match the verity Merkle tree will fail
> +  with EIO (for read()) or SIGBUS (for mmap() reads).
> +
> +- If the sysctl "fs.verity.require_signatures" is set to 1 and the
> +  file's verity measurement is not signed by a key in the fs-verity
> +  keyring, then opening the file will fail.  See `Built-in signature
> +  verification`_.
> +
> +Direct access to the Merkle tree is not supported.  Therefore, if a
> +verity file is copied, or is backed up and restored, then it will lose
> +its "verity"-ness.  fs-verity is primarily meant for files like
> +executables that are managed by a package manager.
> +
> +File measurement computation
> +============================
> +
> +This section describes how fs-verity hashes the file contents using a
> +Merkle tree to produce the "file measurement" which cryptographically
> +identifies the file contents.  This algorithm is the same for all
> +filesystems that support fs-verity.
> +
> +Userspace only needs to be aware of this algorithm if it needs to
> +compute the file measurement itself, e.g. in order to sign the file.
> +
> +Merkle tree
> +-----------
> +
> +The file contents is divided into blocks, where the block size is
> +configurable but is usually 4096 bytes.  The end of the last block is
> +zero-padded if needed.  Each block is then hashed, producing the first
> +level of hashes.  Then, the hashes in this first level are grouped
> +into 'blocksize'-byte blocks (zero-padding the ends as needed) and
> +these blocks are hashed, producing the second level of hashes.  This
> +proceeds up the tree until only a single block remains.  The hash of
> +this block is the "Merkle tree root hash".
> +
> +If the file is nonempty and fits in one block, then the "Merkle tree
> +root hash" is simply the hash of the single data block.  If the file
> +is empty, then the "Merkle tree root hash" is all zeroes.
> +
> +The "blocks" here are not necessarily the same as "filesystem blocks".
> +
> +If a salt was specified, then it's zero-padded to the closest multiple
> +of the input size of the hash algorithm's compression function, e.g.
> +64 bytes for SHA-256 or 128 bytes for SHA-512.  The padded salt is
> +prepended to every data or Merkle tree block that is hashed.
> +
> +The purpose of the block padding is to cause every hash to be taken
> +over the same amount of data, which simplifies the implementation and
> +keeps open more possibilities for hardware acceleration.  The purpose
> +of the salt padding is to make the salting "free" when the salted hash
> +state is precomputed, then imported for each hash.
> +
> +Example: in the recommended configuration of SHA-256 and 4K blocks,
> +128 hash values fit in each block.  Thus, each level of the Merkle
> +tree is approximately 128 times smaller than the previous, and for
> +large files the Merkle tree's size converges to approximately 1/127 of
> +the original file size.  However, for small files, the padding is
> +significant, making the space overhead proportionally more.
> +
> +fs-verity descriptor
> +--------------------
> +
> +By itself, the Merkle tree root hash is ambiguous.  For example, it
> +can't a distinguish a large file from a small second file whose data
> +is exactly the top-level hash block of the first file.  Ambiguities
> +also arise from the convention of padding to the next block boundary.
> +
> +To solve this problem, the verity file measurement is actually
> +computed as a hash of the following structure, which contains the
> +Merkle tree root hash as well as other fields such as the file size::
> +
> +    struct fsverity_descriptor {
> +            __u8 version;           /* must be 1 */
> +            __u8 hash_algorithm;    /* Merkle tree hash algorithm */
> +            __u8 log_blocksize;     /* log2 of size of data and tree blocks */
> +            __u8 salt_size;         /* size of salt in bytes; 0 if none */
> +            __le32 sig_size;        /* must be 0 */
> +            __le64 data_size;       /* size of file the Merkle tree is built over */
> +            __u8 root_hash[64];     /* Merkle tree root hash */
> +            __u8 salt[32];          /* salt prepended to each hashed block */
> +            __u8 __reserved[144];   /* must be 0's */
> +    };
> +
> +Note that the ``sig_size`` field must be set to 0 for the purpose of
> +computing the file measurement, even if a signature was provided (or
> +will be provided) to `FS_IOC_ENABLE_VERITY`_.
> +
> +Built-in signature verification
> +===============================
> +
> +With CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y, fs-verity supports putting
> +a portion of an authentication policy (see `Use cases`_) in the
> +kernel.  Specifically, it adds support for:
> +
> +1. At fs-verity module initialization time, a keyring ".fs-verity" is
> +   created.  The root user can add trusted X.509 certificates to this
> +   keyring using the add_key() system call, then (when done)
> +   optionally use keyctl_restrict_keyring() to prevent additional
> +   certificates from being added.
> +
> +2. `FS_IOC_ENABLE_VERITY`_ accepts a pointer to a PKCS#7 formatted
> +   signature in DER format of the file measurement.  On success, this
> +   signature is persisted alongside the Merkle tree.  Then, any time
> +   the file is opened, the kernel will verify this signature against
> +   the certificates in the ".fs-verity" keyring, and verify that it
> +   matches the actual file measurement.
> +
> +3. A new sysctl "fs.verity.require_signatures" is made available.
> +   When set to 1, the kernel requires that all verity files have a
> +   correctly signed file measurement as described in (2).
> +
> +File measurements must be signed in the following format, which is
> +similar to the structure used by `FS_IOC_MEASURE_VERITY`_::
> +
> +    struct fsverity_signed_digest {
> +            char magic[8];                  /* must be "FSVerity" */
> +            __le16 digest_algorithm;
> +            __le16 digest_size;
> +            __u8 digest[];
> +    };
> +
> +fs-verity's built-in signature verification support is meant as a
> +relatively simple mechanism that can be used to provide some level of
> +authenticity protection for verity files, as an alternative to doing
> +the signature verification in userspace or using IMA-appraisal.
> +However, with this mechanism, userspace programs still need to check
> +that the verity bit is set, and there is no protection against verity
> +files being swapped around.
> +
> +Filesystem support
> +==================
> +
> +fs-verity is currently supported by the ext4 and f2fs filesystems.
> +The CONFIG_FS_VERITY kconfig option must be enabled to use fs-verity
> +on either filesystem.
> +
> +``include/linux/fsverity.h`` declares the interface between the
> +``fs/verity/`` support layer and filesystems.  Briefly, filesystems
> +must provide an ``fsverity_operations`` structure that provides
> +methods to read and write the verity metadata to a filesystem-specific
> +location, including the Merkle tree blocks and
> +``fsverity_descriptor``.  Filesystems must also call functions in
> +``fs/verity/`` at certain times, such as when a file is opened or when
> +pages have been read into the pagecache.  (See `Verifying data`_.)
> +
> +ext4
> +----
> +
> +ext4 supports fs-verity since Linux TODO and e2fsprogs v1.45.2.
> +
> +To create verity files on an ext4 filesystem, the filesystem must have
> +been formatted with ``-O verity`` or had ``tune2fs -O verity`` run on
> +it.  "verity" is an RO_COMPAT filesystem feature, so once set, old
> +kernels will only be able to mount the filesystem readonly, and old
> +versions of e2fsck will be unable to check the filesystem.  Moreover,
> +currently ext4 only supports mounting a filesystem with the "verity"
> +feature when its block size is equal to PAGE_SIZE (often 4096 bytes).
> +
> +ext4 sets the EXT4_VERITY_FL on-disk inode flag on verity files.  It
> +can only be set by `FS_IOC_ENABLE_VERITY`_, and it cannot be cleared.
> +
> +ext4 also supports encryption, which can be used simultaneously with
> +fs-verity.  In this case, the plaintext data is verified rather than
> +the ciphertext.  This is necessary in order to make the file
> +measurement meaningful, since every file is encrypted differently.
> +
> +ext4 stores the verity metadata (Merkle tree and fsverity_descriptor)
> +past the end of the file, starting at the first 64K boundary beyond
> +i_size.  This approach works because (a) verity files are readonly,
> +and (b) pages fully beyond i_size aren't visible to userspace but can
> +be read/written internally by ext4 with only some relatively small
> +changes to ext4.  This approach avoids having to depend on the
> +EA_INODE feature and on rearchitecturing ext4's xattr support to
> +support paging multi-gigabyte xattrs into memory, and to support
> +encrypting xattrs.  Note that the verity metadata *must* be encrypted
> +when the file is, since it contains hashes of the plaintext data.
> +
> +Currently, ext4 verity only supports the case where the Merkle tree
> +block size, filesystem block size, and page size are all the same.  It
> +also only supports extent-based files.
> +
> +f2fs
> +----
> +
> +f2fs supports fs-verity since Linux TODO and f2fs-tools v1.11.0.
> +
> +To create verity files on an f2fs filesystem, the filesystem must have
> +been formatted with ``-O verity``.
> +
> +f2fs sets the FADVISE_VERITY_BIT on-disk inode flag on verity files.
> +It can only be set by `FS_IOC_ENABLE_VERITY`_, and it cannot be
> +cleared.
> +
> +Like ext4, f2fs stores the verity metadata (Merkle tree and
> +fsverity_descriptor) past the end of the file, starting at the first
> +64K boundary beyond i_size.  See explanation for ext4 above.
> +Moreover, f2fs supports at most 4096 bytes of xattr entries per inode
> +which wouldn't be enough for even a single Merkle tree block.
> +
> +Currently, f2fs verity only supports a Merkle tree block size of 4096.
> +
> +Implementation details
> +======================
> +
> +Verifying data
> +--------------
> +
> +fs-verity ensures that all reads of a verity file's data are verified,
> +regardless of which syscall is used to do the read (e.g. mmap(),
> +read(), pread()) and regardless of whether it's the first read or a
> +later read (unless the later read can return cached data that was
> +already verified).  Below, we describe how filesystems implement this.
> +
> +Pagecache
> +~~~~~~~~~
> +
> +For filesystems using Linux's pagecache, the ``->readpage()`` and
> +``->readpages()`` methods must be modified to verify pages before they
> +are marked Uptodate.  Merely hooking ``->read_iter()`` would be
> +insufficient, since ``->read_iter()`` is not used for memory maps.
> +
> +Therefore, fs/verity/ provides a function fsverity_verify_page() which
> +verifies a page that has been read into the pagecache of a verity
> +inode, but is still locked and not Uptodate, so it's not yet readable
> +by userspace.  As needed to do the verification,
> +fsverity_verify_page() will call back into the filesystem to read
> +Merkle tree pages via fsverity_operations::read_merkle_tree_page().
> +
> +fsverity_verify_page() returns false if verification failed; in this
> +case, the filesystem must not set the page Uptodate.  Following this,
> +as per the usual Linux pagecache behavior, attempts by userspace to
> +read() from the part of the file containing the page will fail with
> +EIO, and accesses to the page within a memory map will raise SIGBUS.
> +
> +fsverity_verify_page() currently only supports the case where the
> +Merkle tree block size is equal to PAGE_SIZE (often 4096 bytes).
> +
> +In principle, fsverity_verify_page() verifies the entire path in the
> +Merkle tree from the data page to the root hash.  However, for
> +efficiency the filesystem may cache the hash pages.  Therefore,
> +fsverity_verify_page() only ascends the tree reading hash pages until
> +an already-verified hash page is seen, as indicated by the PageChecked
> +bit being set.  It then verifies the path to that page.
> +
> +This optimization, which is also used by dm-verity, results in
> +excellent sequential read performance.  This is because usually (e.g.
> +127 in 128 times for 4K blocks and SHA-256) the hash page from the
> +bottom level of the tree will already be cached and checked from
> +reading a previous data page.  However, random reads perform worse.
> +
> +Block device based filesystems
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Block device based filesystems (e.g. ext4 and f2fs) in Linux also use
> +the pagecache, so the above subsection applies too.  However, they
> +also usually read many pages from a file at once, grouped into a
> +structure called a "bio".  To make it easier for these types of
> +filesystems to support fs-verity, fs/verity/ also provides a function
> +fsverity_verify_bio() which verifies all pages in a bio.
> +
> +ext4 and f2fs also support encryption.  If a verity file is also
> +encrypted, the pages must be decrypted before being verified.  To
> +support this, these filesystems allocate a "post-read context" for
> +each bio and store it in ``->bi_private``::
> +
> +    struct bio_post_read_ctx {
> +           struct bio *bio;
> +           struct work_struct work;
> +           unsigned int cur_step;
> +           unsigned int enabled_steps;
> +    };
> +
> +``enabled_steps`` is a bitmask that specifies whether decryption,
> +verity, or both is enabled.  After the bio completes, for each needed
> +postprocessing step the filesystem enqueues the bio_post_read_ctx on a
> +workqueue, and then the workqueue work does the decryption or
> +verification.  Finally, pages where no decryption or verity error
> +occurred are marked Uptodate, and the pages are unlocked.
> +
> +Files on ext4 and f2fs may contain holes.  Normally, ``->readpages()``
> +simply zeroes holes and sets the corresponding pages Uptodate; no bios
> +are issued.  To prevent this case from bypassing fs-verity, these
> +filesystems use fsverity_verify_page() to verify hole pages.
> +
> +ext4 and f2fs disable direct I/O on verity files, since otherwise
> +direct I/O would bypass fs-verity.  (They also do the same for
> +encrypted files.)
> +
> +Userspace utility
> +=================
> +
> +This document focuses on the kernel, but a userspace utility for
> +fs-verity can be found at:
> +
> +	https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git
> +
> +See the README.md file in the fsverity-utils source tree for details,
> +including examples of setting up fs-verity protected files.
> +
> +Tests
> +=====
> +
> +To test fs-verity, use xfstests.  For example, using `kvm-xfstests
> +<https://github.com/tytso/xfstests-bld/blob/master/Documentation/kvm-quickstart.md>`_::
> +
> +    kvm-xfstests -c ext4,f2fs -g verity
> +
> +FAQ
> +===
> +
> +This section answers frequently asked questions about fs-verity that
> +weren't already directly answered in other parts of this document.
> +
> +:Q: Why isn't fs-verity part of IMA?
> +:A: fs-verity and IMA (Integrity Measurement Architecture) have
> +    different focuses.  fs-verity is a filesystem-level mechanism for
> +    hashing individual files using a Merkle tree.  In contrast, IMA
> +    specifies a system-wide policy that specifies which files are
> +    hashed and what to do with those hashes, such as log them,
> +    authenticate them, or add them to a measurement list.
> +
> +    IMA is planned to support the fs-verity hashing mechanism as an
> +    alternative to doing full file hashes, for people who want the
> +    performance and security benefits of the Merkle tree based hash.
> +    But it doesn't make sense to force all uses of fs-verity to be
> +    through IMA.  As a standalone filesystem feature, fs-verity
> +    already meets many users' needs, and it's testable like other
> +    filesystem features e.g. with xfstests.
> +
> +:Q: Isn't fs-verity useless because the attacker can just modify the
> +    hashes in the Merkle tree, which is stored on-disk?
> +:A: To verify the authenticity of an fs-verity file you must verify
> +    the authenticity of the "file measurement", which is basically the
> +    root hash of the Merkle tree.  See `Use cases`_.
> +
> +:Q: Isn't fs-verity useless because the attacker can just replace a
> +    verity file with a non-verity one?
> +:A: See `Use cases`_.  In the initial use case, it's really trusted
> +    userspace code that authenticates the files; fs-verity is just a
> +    tool to do this job efficiently and securely.  The trusted
> +    userspace code will consider non-verity files to be inauthentic.
> +
> +:Q: Why does the Merkle tree need to be stored on-disk?  Couldn't you
> +    store just the root hash?
> +:A: If the Merkle tree wasn't stored on-disk, then you'd have to
> +    compute the entire tree when the file is first accessed, even if
> +    just one byte is being read.  This is a fundamental consequence of
> +    how Merkle tree hashing works.  To verify a leaf node, you need to
> +    verify the whole path to the root hash, including the root node
> +    (the thing which the root hash is a hash of).  But if the root
> +    node isn't stored on-disk, you have to compute it by hashing its
> +    children, and so on until you've actually hashed the entire file.
> +
> +    That defeats most of the point of doing a Merkle tree-based hash,
> +    since if you have to hash the whole file ahead of time anyway,
> +    then you could simply do sha256(file) instead.  That would be much
> +    simpler, and a bit faster too.
> +
> +    It's true that an in-memory Merkle tree could still provide the
> +    advantage of verification on every read rather than just on the
> +    first read.  However, it would be inefficient because every time a
> +    hash page gets evicted (you can't pin the entire Merkle tree into
> +    memory, since it may be very large), in order to restore it you
> +    again need to hash everything below it in the tree.  This again
> +    defeats most of the point of doing a Merkle tree-based hash, since
> +    a single block read could trigger re-hashing gigabytes of data.
> +
> +:Q: But couldn't you store just the leaf nodes and compute the rest?
> +:A: See previous answer; this really just moves up one level, since
> +    one could alternatively interpret the data blocks as being the
> +    leaf nodes of the Merkle tree.  It's true that the tree can be
> +    computed much faster if the leaf level is stored rather than just
> +    the data, but that's only because each level is less than 1% the
> +    size of the level below (assuming the recommended settings of
> +    SHA-256 and 4K blocks).  For the exact same reason, by storing
> +    "just the leaf nodes" you'd already be storing over 99% of the
> +    tree, so you might as well simply store the whole tree.
> +
> +:Q: Can the Merkle tree be built ahead of time, e.g. distributed as
> +    part of a package that is installed to many computers?
> +:A: This isn't currently supported.  It was part of the original
> +    design, but was removed to simplify the kernel UAPI and because it
> +    wasn't a critical use case.  Files are usually installed once and
> +    used many times, and cryptographic hashing is somewhat fast on
> +    most modern processors.
> +
> +:Q: Why doesn't fs-verity support writes?
> +:A: Write support would be very difficult and would require a
> +    completely different design, so it's well outside the scope of
> +    fs-verity.  Write support would require:
> +
> +    - A way to maintain consistency between the data and hashes,
> +      including all levels of hashes, since corruption after a crash
> +      (especially of potentially the entire file!) is unacceptable.
> +      The main options for solving this are data journalling,
> +      copy-on-write, and log-structured volume.  But it's very hard to
> +      retrofit existing filesystems with new consistency mechanisms.
> +      Data journalling is available on ext4, but is very slow.
> +
> +    - Rebuilding the the Merkle tree after every write, which would be
> +      extremely inefficient.  Alternatively, a different authenticated
> +      dictionary structure such as an "authenticated skiplist" could
> +      be used.  However, this would be far more complex.
> +
> +    Compare it to dm-verity vs. dm-integrity.  dm-verity is very
> +    simple: the kernel just verifies read-only data against a
> +    read-only Merkle tree.  In contrast, dm-integrity supports writes
> +    but is slow, is much more complex, and doesn't actually support
> +    full-device authentication since it authenticates each sector
> +    independently, i.e. there is no "root hash".  It doesn't really
> +    make sense for the same device-mapper target to support these two
> +    very different cases; the same applies to fs-verity.
> +
> +:Q: Since verity files are immutable, why isn't the immutable bit set?
> +:A: The existing "immutable" bit (FS_IMMUTABLE_FL) already has a
> +    specific set of semantics which not only make the file contents
> +    read-only, but also prevent the file from being deleted, renamed,
> +    linked to, or having its owner or mode changed.  These extra
> +    properties are unwanted for fs-verity, so reusing the immutable
> +    bit isn't appropriate.
> +
> +:Q: Why does the API use ioctls instead of setxattr() and getxattr()?
> +:A: Abusing the xattr interface for basically arbitrary syscalls is
> +    heavily frowned upon by most of the Linux filesystem developers.
> +    An xattr should really just be an xattr on-disk, not an API to
> +    e.g. magically trigger construction of a Merkle tree.
> +
> +:Q: Does fs-verity support remote filesystems?
> +:A: Only ext4 and f2fs support is implemented currently, but in
> +    principle any filesystem that can store per-file verity metadata
> +    can support fs-verity, regardless of whether it's local or remote.
> +    Some filesystems may have fewer options of where to store the
> +    verity metadata; one possibility is to store it past the end of
> +    the file and "hide" it from userspace by manipulating i_size.  The
> +    data verification functions provided by ``fs/verity/`` also assume
> +    that the filesystem uses the Linux pagecache, but both local and
> +    remote filesystems normally do so.
> +
> +:Q: Why is anything filesystem-specific at all?  Shouldn't fs-verity
> +    be implemented entirely at the VFS level?
> +:A: There are many reasons why this is not possible or would be very
> +    difficult, including the following:
> +
> +    - To prevent bypassing verification, pages must not be marked
> +      Uptodate until they've been verified.  Currently, each
> +      filesystem is responsible for marking pages Uptodate via
> +      ``->readpages()``.  Therefore, currently it's not possible for
> +      the VFS to do the verification on its own.  Changing this would
> +      require significant changes to the VFS and all filesystems.
> +
> +    - It would require defining a filesystem-independent way to store
> +      the verity metadata.  Extended attributes don't work for this
> +      because (a) the Merkle tree may be gigabytes, but many
> +      filesystems assume that all xattrs fit into a single 4K
> +      filesystem block, and (b) ext4 and f2fs encryption doesn't
> +      encrypt xattrs, yet the Merkle tree *must* be encrypted when the
> +      file contents are, because it stores hashes of the plaintext
> +      file contents.
> +
> +      So the verity metadata would have to be stored in an actual
> +      file.  Using a separate file would be very ugly, since the
> +      metadata is fundamentally part of the file to be protected, and
> +      it could cause problems where users could delete the real file
> +      but not the metadata file or vice versa.  On the other hand,
> +      having it be in the same file would break applications unless
> +      filesystems' notion of i_size were divorced from the VFS's,
> +      which would be complex and require changes to all filesystems.
> +
> +    - It's desirable that FS_IOC_ENABLE_VERITY uses the filesystem's
> +      transaction mechanism so that either the file ends up with
> +      verity enabled, or no changes were made.  Allowing intermediate
> +      states to occur after a crash may cause problems.
> diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst
> index 1131c34d77f6f1..416c7f0e123af7 100644
> --- a/Documentation/filesystems/index.rst
> +++ b/Documentation/filesystems/index.rst
> @@ -31,6 +31,7 @@ filesystem implementations.
>  
>     journalling
>     fscrypt
> +   fsverity
>  
>  Filesystem-specific documentation
>  =================================
> -- 
> 2.22.0.410.gd8fdbe21b5-goog



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux