1 # SPDX-License-Identifier: GPL-2.0
2 # Copyright 2018 Google LLC
4 # Functions for setting up and testing fs-verity
6 _require_scratch_verity()
9 _require_command "$FSVERITY_PROG" fsverity
11 if ! _scratch_mkfs_verity &>>$seqres.full; then
12 # ext4: need e2fsprogs v1.44.5 or later (but actually v1.45.2+
13 # is needed for some tests to pass, due to an e2fsck bug)
14 # f2fs: need f2fs-tools v1.11.0 or later
15 _notrun "$FSTYP userspace tools don't support fs-verity"
18 # Try to mount the filesystem. If this fails then either the kernel
19 # isn't aware of fs-verity, or the mkfs options were not compatible with
20 # verity (e.g. ext4 with block size != PAGE_SIZE).
21 if ! _try_scratch_mount &>>$seqres.full; then
22 _notrun "kernel is unaware of $FSTYP verity feature," \
23 "or mkfs options are not compatible with verity"
26 # The filesystem may be aware of fs-verity but have it disabled by
27 # CONFIG_FS_VERITY=n. Detect support via sysfs.
28 if [ ! -e /sys/fs/$FSTYP/features/verity ]; then
29 _notrun "kernel $FSTYP isn't configured with verity support"
32 # The filesystem may have fs-verity enabled but not actually usable by
33 # default. E.g., ext4 only supports verity on extent-based files, so it
34 # doesn't work on ext3-style filesystems. So, try actually using it.
35 echo foo > $SCRATCH_MNT/tmpfile
36 _disable_fsverity_signatures
37 if ! _fsv_enable $SCRATCH_MNT/tmpfile; then
38 _restore_fsverity_signatures
39 _notrun "$FSTYP verity isn't usable by default with these mkfs options"
41 _restore_fsverity_signatures
42 rm -f $SCRATCH_MNT/tmpfile
46 # Merkle tree block size. Currently all filesystems only support
47 # PAGE_SIZE for this. This is also the default for 'fsverity enable'.
48 FSV_BLOCK_SIZE=$(get_page_size)
51 # Check for CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y, as well as the userspace
52 # commands needed to generate certificates and add them to the kernel.
53 _require_fsverity_builtin_signatures()
55 if [ ! -e /proc/sys/fs/verity/require_signatures ]; then
56 _notrun "kernel doesn't support fs-verity builtin signatures"
58 _require_command "$OPENSSL_PROG" openssl
59 _require_command "$KEYCTL_PROG" keyctl
62 # Use the openssl program to generate a private key and a X.509 certificate for
63 # use with fs-verity built-in signature verification, and convert the
64 # certificate to DER format.
71 if ! $OPENSSL_PROG req -newkey rsa:4096 -nodes -batch -x509 \
72 -keyout $keyfile -out $certfile &>> $seqres.full; then
73 _fail "Failed to generate certificate and private key (see $seqres.full)"
75 $OPENSSL_PROG x509 -in $certfile -out $certfileder -outform der
78 # Clear the .fs-verity keyring.
81 $KEYCTL_PROG clear %keyring:.fs-verity
84 # Load the given X.509 certificate in DER format into the .fs-verity keyring so
85 # that the kernel can use it to verify built-in signatures.
90 $KEYCTL_PROG padd asymmetric '' %keyring:.fs-verity \
91 < $certfileder >> $seqres.full
94 # Disable mandatory signatures for fs-verity files, if they are supported.
95 _disable_fsverity_signatures()
97 if [ -e /proc/sys/fs/verity/require_signatures ]; then
98 if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
99 FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
101 echo 0 > /proc/sys/fs/verity/require_signatures
105 # Enable mandatory signatures for fs-verity files.
106 # This assumes that _require_fsverity_builtin_signatures() was called.
107 _enable_fsverity_signatures()
109 if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
110 FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
112 echo 1 > /proc/sys/fs/verity/require_signatures
115 # Restore the original signature verification setting.
116 _restore_fsverity_signatures()
118 if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
119 echo "$FSVERITY_SIG_CTL_ORIG" > /proc/sys/fs/verity/require_signatures
123 # Require userspace and kernel support for 'fsverity dump_metadata'.
124 # $1 must be a file with fs-verity enabled.
125 _require_fsverity_dump_metadata()
128 local tmpfile=$tmp.require_fsverity_dump_metadata
130 if _fsv_dump_merkle_tree "$verity_file" 2>"$tmpfile" >/dev/null; then
133 if grep -q "^ERROR: unrecognized command: 'dump_metadata'$" "$tmpfile"
135 _notrun "Missing 'fsverity dump_metadata' command"
137 if grep -q "^ERROR: FS_IOC_READ_VERITY_METADATA failed on '.*': Inappropriate ioctl for device$" "$tmpfile"
139 _notrun "Kernel doesn't support FS_IOC_READ_VERITY_METADATA"
141 _fail "Unexpected output from 'fsverity dump_metadata': $(<"$tmpfile")"
144 _scratch_mkfs_verity()
148 _scratch_mkfs -O verity
151 _notrun "No verity support for $FSTYP"
156 _scratch_mkfs_encrypted_verity()
160 _scratch_mkfs -O encrypt,verity
163 # f2fs-tools as of v1.11.0 doesn't allow comma-separated
164 # features with -O. Instead -O must be supplied multiple times.
165 _scratch_mkfs -O encrypt -O verity
168 _notrun "$FSTYP not supported in _scratch_mkfs_encrypted_verity"
173 _fsv_scratch_begin_subtest()
177 rm -rf "${SCRATCH_MNT:?}"/*
181 _fsv_dump_merkle_tree()
183 $FSVERITY_PROG dump_metadata merkle_tree "$@"
186 _fsv_dump_descriptor()
188 $FSVERITY_PROG dump_metadata descriptor "$@"
191 _fsv_dump_signature()
193 $FSVERITY_PROG dump_metadata signature "$@"
198 $FSVERITY_PROG enable "$@"
203 $FSVERITY_PROG measure "$@" | awk '{print $1}'
208 $FSVERITY_PROG sign "$@"
211 # Generate a file, then enable verity on it.
212 _fsv_create_enable_file()
217 head -c $((FSV_BLOCK_SIZE * 2)) /dev/zero > "$file"
218 _fsv_enable "$file" "$@"
221 _fsv_have_hash_algorithm()
227 head -c 4096 /dev/zero > $test_file
228 if ! _fsv_enable --hash-alg=$hash_alg $test_file &>> $seqres.full; then
237 # _fsv_scratch_corrupt_bytes - Write some bytes to a file, bypassing the filesystem
239 # Write the bytes sent on stdin to the given offset in the given file, but do so
240 # by writing directly to the extents on the block device, with the filesystem
241 # unmounted. This can be used to corrupt a verity file for testing purposes,
242 # bypassing the restrictions imposed by the filesystem.
244 # The file is assumed to be located on $SCRATCH_DEV.
246 _fsv_scratch_corrupt_bytes()
250 local lstart lend pstart pend
254 sync # Sync to avoid unwritten extents
257 local end=$(( offset + $(_get_filesize $tmp.bytes ) ))
259 # For each extent that intersects the requested range in order, add a
260 # command that writes the next part of the data to that extent.
261 while read -r lstart lend pstart pend; do
262 lstart=$((lstart * 512))
263 lend=$(((lend + 1) * 512))
264 pstart=$((pstart * 512))
265 pend=$(((pend + 1) * 512))
267 if (( lend - lstart != pend - pstart )); then
268 _fail "Logical and physical extent lengths differ for file '$file'"
269 elif (( offset < lstart )); then
270 _fail "Hole in file '$file' at byte $offset. Next extent begins at byte $lstart"
271 elif (( offset < lend )); then
272 local len=$((lend - offset))
273 local seek=$((pstart + (offset - lstart)))
274 dd_cmds+=("head -c $len | dd of=$SCRATCH_DEV oflag=seek_bytes seek=$seek status=none")
277 done < <($XFS_IO_PROG -r -c "fiemap $offset $((end - offset))" "$file" \
278 | _filter_xfs_io_fiemap)
280 if (( offset < end )); then
281 _fail "Extents of file '$file' ended at byte $offset, but needed until $end"
284 # Execute the commands to write the data
286 for cmd in "${dd_cmds[@]}"; do
289 sync # Sync to flush the block device's pagecache
294 # _fsv_scratch_corrupt_merkle_tree - Corrupt a file's Merkle tree
296 # Like _fsv_scratch_corrupt_bytes(), but this corrupts the file's fs-verity
297 # Merkle tree. The offset is given as a byte offset into the Merkle tree.
299 _fsv_scratch_corrupt_merkle_tree()
306 # ext4 and f2fs store the Merkle tree after the file contents
307 # itself, starting at the next 65536-byte aligned boundary.
308 (( offset += ($(_get_filesize $file) + 65535) & ~65535 ))
309 _fsv_scratch_corrupt_bytes $file $offset
312 _fail "_fsv_scratch_corrupt_merkle_tree() unimplemented on $FSTYP"