|
|
Subscribe / Log in / New account

Yet another try for fs-verity

By Jonathan Corbet
June 3, 2019
The fs‑verity mechanism has its origins in the Android project; its purpose is to make individual files read-only and enable the kernel to detect any modifications that might have been made, even if those changes happen offline. Previous fs‑verity implementations have run into criticism in the development community, and none have been merged. A new version of the patch set was posted on May 23; it features a changed user-space API and may have a better chance of getting into the mainline.

Fs‑verity works by associating a set of hashes with a file; the hash values can be used to check that the contents of the file have not been changed. In current implementations, the hashes are stored in a Merkle tree, which allows for quick verification when the file is accessed. The tree itself is hashed and signed, so modifications to the hash values can also be detected (and access to the file blocked). The intended use case is to protect critical Android packages even when an attacker is able to make changes to the local storage device.

Previous versions of the fs‑verity patches ran aground over objections to how the API worked. To protect a file, user space would need to generate and sign a Merkle tree, then append that tree to the file itself, aligned to the beginning of a filesystem block. After an ioctl() call, the kernel would hide the tree, making the file appear to be shorter than it really was, while using the tree to verify the file's contents. This mechanism was seen as being incompatible with how some filesystems manage space at the end of files; developers also complained that it exposed too much about how fs‑verity was implemented internally. In the end, an attempt to merge this code for 5.0 was not acted upon, and fs‑verity remained outside of the mainline.

The new patch set addresses these concerns by moving the generation of the Merkle tree into the kernel and hiding the details of where this tree is stored. To enable fs‑verity protection for a file, a user-space application starts by opening the file in question. Despite the fact that this operation changes the file (by adding the protection and making the file read-only), this file descriptor must be opened for read access only. Then, the new FS_IOC_ENABLE_VERITY ioctl() command is invoked on this file; the application passes in a structure that looks like this:

    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];
    };

The version field must be set to one; it is there to allow different fs‑verity implementations in the future. Similarly, the reserved fields must all be set to zero. hash_algorithm tells the kernel which algorithm to use for hashing the file's blocks; the only supported values at the moment are FS_VERITY_HASH_ALG_SHA256 and FS_VERITY_HASH_ALG_SHA512. The block size for the hash is set in block_size; it must match the filesystem block size. If salt_size and salt_ptr are set, they provide a "salt" value that is prepended to each block prior to hashing. A digital signature for the hash of the file can optionally be added using sig_ptr and sig_size; more on that shortly.

This ioctl() call will read through the entire file, generating the Merkle tree and storing it wherever the filesystem thinks is best. If the file is large, this operation can take some time; it can be interrupted with a fatal signal, leaving the file unchanged. Enabling fs‑verity will fail if there are any open, write-enabled file descriptors for the target file.

After the operation succeeds, the file will be in the fs‑verity mode. Opens for write access will fail, even if the file's permission bits would otherwise allow writing. Some metadata can still be changed, though, and the file can be renamed or deleted. Any attempt to read from the file will fail (with EIO) if the data of interest does not match the stored hash. If user space is counting on fs‑verity protection, though, it should, after opening the file, verify that this protection is present with the FS_IOC_MEASURE_VERITY ioctl() call, which takes a pointer to this structure:

    struct fsverity_digest {
	__u16 digest_algorithm;
	__u16 digest_size; /* input/output */
	__u8 digest[];
    };

If the file is protected with fs‑verity, this structure will be filled in with summary hash information.

User space can use that information to verify that the digest data matches expectations; without that test, an attacker could substitute a new file with hostile contents and a matching Merkle tree. Alternatively, this digest can be signed and the kernel will verify that it matches at access time. What must actually be signed is this structure:

    struct fsverity_signed_digest {
	char magic[8];                  /* must be "FSVerity" */
	__le16 digest_algorithm;
	__le16 digest_size;
	__u8 digest[];
    };

The digest information can be obtained from the kernel using the FS_IOC_MEASURE_VERITY ioctl() described just above. So one way to add a signature to an fs‑verity file would be to create the file once, enable fs‑verity on the file without a signature, obtain the digest information, then create and enable the file a second time with the signature data. In practice, files to be protected this way (such as Android package files) will probably be shipped with the associated signature data, so this two-step process will not be necessary on the target systems.

The final piece for signature verification is the provision of a public key to verify against. The fs‑verity subsystem creates a new keyring (called .fs‑verity); a suitably privileged user can add certificates to this keyring for use in file verification. The signing key, of course, should not be on the target system at all; assuming that the attacker cannot obtain that key by other means, verification against the public key should provide assurance that the file has not been modified.

The ext4 and F2FS filesystems are supported in the current patch set. See the extensive documentation file provided for the patch set for a lot more details on how it all works. Some kernel features are added without sufficient documentation; fs‑verity does not look like it will be one of those.

Previous versions of the patch set have generated a lot of (sometimes heated) discussion. This time, the response has been silent, prompting Eric Biggers (the author of this work) to ask if anybody has any comments. Unless somebody shows up with objections, the logical conclusion is that the biggest concerns have been addressed and that fs‑verity may be on track for merging into the 5.3 kernel.

Index entries for this article
KernelFilesystems/fs-verity
KernelSecurity/Integrity verification
SecurityIntegrity management


to post comments

Yet another try for fs-verity

Posted Jun 3, 2019 22:07 UTC (Mon) by bjartur (guest, #67801) [Link] (1 responses)

Will processes with the file already open for writing still be able to write to the file after the ioctl returns?

Yet another try for fs-verity

Posted Jun 3, 2019 22:14 UTC (Mon) by corbet (editor, #1) [Link]

Yes because the call will fall in that case.

Yet another try for fs-verity

Posted Jun 3, 2019 23:07 UTC (Mon) by Cyberax (✭ supporter ✭, #52523) [Link] (3 responses)

Is there a way to verify the directory structure as well?

Yet another try for fs-verity

Posted Jun 4, 2019 3:09 UTC (Tue) by ebiggers (subscriber, #130760) [Link] (2 responses)

The fs-verity kernel feature only hashes the file contents, and optionally checks whether the hash is validly signed. It's up to trusted userspace code to use this as a tool to enforce a meaningful authentication policy, e.g. the application launcher can check that binaries have the fs-verity bit set before running them. This userspace code may be looking for the files in a specific location, and it can also validate whatever additional metadata it wants to.

Yet another try for fs-verity

Posted Jun 4, 2019 3:56 UTC (Tue) by Cyberax (✭ supporter ✭, #52523) [Link] (1 responses)

Can it, perhaps, also have verified paths as an optional attribute? I understand that it can be done through xattrs, but it'd be nice to have both in one place.

Yet another try for fs-verity

Posted Jun 4, 2019 20:32 UTC (Tue) by ebiggers (subscriber, #130760) [Link]

Sure, but whenever dealing with "paths" in the kernel you have to worry about things like links and mount namespaces. And also paths aren't special; people could also ask for owner, uid, group, mode, ACL, LSM labels, encryption status etc. Support for including these fields in the fs-verity file measurement can be added later if needed. It's best to start with the core feature first, which we know is going to be used. Adding a lot of complex extra functionality early on is risky.

Yet another try for fs-verity

Posted Jun 3, 2019 23:10 UTC (Mon) by skissane (subscriber, #38675) [Link] (1 responses)

This seems like the sort of use case that "alternate data streams" (as in NTFS) or "forks" (as in HFS/HFS+) would be ideally suited for.

I can see from the documentation that they didn't use extended attributes in ext4 or FFS (which in some ways resemble forks/streams) because neither filesystem's extended attribute implementation is scalable to large attribute values, which Merkle trees require, and it is less work to put hidden data at the end of the file than to make the extended attribute implementations scalable.

Since the storage mechanism is per-filesystem, some other filesystem which has scalable extended attributes (or forks or streams) could use that instead.

Yet another try for fs-verity

Posted Jun 4, 2019 10:44 UTC (Tue) by grawity (subscriber, #80596) [Link]

It certainly does, but IIRC the whole idea of alternate streams was met with a strong "no" in the previous fs-verity discussion. :(

Yet another try for fs-verity

Posted Jun 3, 2019 23:53 UTC (Mon) by jhoblitt (subscriber, #77733) [Link] (3 responses)

Why not use an xattr so user space had some way of detecting these magic files without having to try an unusual ioctl on every file. It would also be nice, regardless of the config interface, if a protected file always showed the immutable bit as true.

Yet another try for fs-verity

Posted Jun 4, 2019 0:34 UTC (Tue) by skissane (subscriber, #38675) [Link] (2 responses)

> Why not use an xattr so user space had some way of detecting these magic files without having to try an unusual ioctl on every file

The documentation https://lwn.net/ml/linux-fsdevel/20190523161811.6259-2-eb... explains why this wasn't done:
1) many file systems impose a small limit on the extended attribute value size (e.g. 4096 bytes on F2FS), while these Merkle trees can be very big
2) other file systems support very big extended attribute values in theory, but perform very poorly with them (e.g. ext4)
3) some filesystems (e.g. ext4) support file encryption. Since the Merkle tree is based on the plaintext not the ciphertext, in that case the Merkle tree needs to be encrypted as well. However, while ext4 supports file encryption, it doesn't support the encryption of the extended attributes

All the above flaws in extended attribute support probably can and should be fixed. But it is a lot of work, especially since it is a problem across multiple filesystems. Not using extended attributes for this feature is a way to deliver this feature without being blocked by the need to fix those above flaws.

I guess another option would be to have an extended attribute which indicates the presence of a Merkle tree, but doesn't actually store the Merkle tree data. That would meet your requirement (for an easy way for user space to detect the feature is enabled on any given file) without running up against the above extended attribute limitations.

> It would also be nice, regardless of the config interface, if a protected file always showed the immutable bit as true

I agree with that.

Yet another try for fs-verity

Posted Jun 4, 2019 2:23 UTC (Tue) by jhoblitt (subscriber, #77733) [Link] (1 responses)

What I was suggesting is that the same configuration as is proposed to be passed via ioctl is set as xattrs, while the checksum metadata tree still remains "hidden".

Yet another try for fs-verity

Posted Jun 4, 2019 2:43 UTC (Tue) by ebiggers (subscriber, #130760) [Link]

The documentation covers why the Merkle tree isn't stored in xattrs on ext4 and f2fs. Independently of that, in theory the API *could* use xattrs instead of ioctls, e.g. FS_IOC_ENABLE_VERITY could be replaced with setxattr("system.fsverity", &fsverity_enable_arg), and FS_IOC_MEASURE_VERITY replaced with getxattr("system.fsverity"). However, 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.

We're still planning to expose the verity bit through FS_IOC_GETFLAGS and possibly statx() too, just like the encrypt bit. So detecting verity files will still be straightforward; you don't need xattrs for this.

We aren't using the existing "immutable" bit because it already has specific semantics that include much more than just file contents immutability, e.g. it also prevents the file from being deleted, renamed, linked to, or have its owner or mode changed. So reusing the immutable bit to mean "fs-verity enabled" would not be appropriate.

Yet another try for fs-verity

Posted Jun 6, 2019 5:39 UTC (Thu) by draco (subscriber, #1792) [Link] (1 responses)

I don't like the idea that attempts to write to the file will fail even though the permissions bits allow writing.

Will /bin/ls be tweaked in some way to notify users that the fs-verity mode is set? Currently SELinux contexts add '.' and ACLs add '+' to the end of the permissions string. To my mind, something similar should apply. Perhaps '#' (since the file is hashed)?

Yet another try for fs-verity

Posted Mar 7, 2025 13:45 UTC (Fri) by cybertom (guest, #176384) [Link]

I agree with your statement. For the current fs-verity use case, it seems that we cannot directly see from the file system whether the target file is using the fs-verity feature.


Copyright © 2019, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds