b4c0e2dcb99fa2b439ea88e3662c1ec08cae7e41
[xfstests-dev.git] / common / verity
1 # SPDX-License-Identifier: GPL-2.0
2 # Copyright 2018 Google LLC
3 #
4 # Functions for setting up and testing fs-verity
5
6 _require_scratch_verity()
7 {
8         _require_scratch
9         _require_command "$FSVERITY_PROG" fsverity
10
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"
16         fi
17
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"
24         fi
25
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"
30         fi
31
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"
40         fi
41         _restore_fsverity_signatures
42         rm -f $SCRATCH_MNT/tmpfile
43
44         _scratch_unmount
45
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)
49 }
50
51 # Check for CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y.
52 _require_fsverity_builtin_signatures()
53 {
54         if [ ! -e /proc/sys/fs/verity/require_signatures ]; then
55                 _notrun "kernel doesn't support fs-verity builtin signatures"
56         fi
57 }
58
59 # Disable mandatory signatures for fs-verity files, if they are supported.
60 _disable_fsverity_signatures()
61 {
62         if [ -e /proc/sys/fs/verity/require_signatures ]; then
63                 if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
64                         FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
65                 fi
66                 echo 0 > /proc/sys/fs/verity/require_signatures
67         fi
68 }
69
70 # Enable mandatory signatures for fs-verity files.
71 # This assumes that _require_fsverity_builtin_signatures() was called.
72 _enable_fsverity_signatures()
73 {
74         if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
75                 FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
76         fi
77         echo 1 > /proc/sys/fs/verity/require_signatures
78 }
79
80 # Restore the original signature verification setting.
81 _restore_fsverity_signatures()
82 {
83         if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
84                 echo "$FSVERITY_SIG_CTL_ORIG" > /proc/sys/fs/verity/require_signatures
85         fi
86 }
87
88 _scratch_mkfs_verity()
89 {
90         case $FSTYP in
91         ext4|f2fs)
92                 _scratch_mkfs -O verity
93                 ;;
94         *)
95                 _notrun "No verity support for $FSTYP"
96                 ;;
97         esac
98 }
99
100 _scratch_mkfs_encrypted_verity()
101 {
102         case $FSTYP in
103         ext4)
104                 _scratch_mkfs -O encrypt,verity
105                 ;;
106         f2fs)
107                 # f2fs-tools as of v1.11.0 doesn't allow comma-separated
108                 # features with -O.  Instead -O must be supplied multiple times.
109                 _scratch_mkfs -O encrypt -O verity
110                 ;;
111         *)
112                 _notrun "$FSTYP not supported in _scratch_mkfs_encrypted_verity"
113                 ;;
114         esac
115 }
116
117 _fsv_scratch_begin_subtest()
118 {
119         local msg=$1
120
121         rm -rf "${SCRATCH_MNT:?}"/*
122         echo -e "\n# $msg"
123 }
124
125 _fsv_enable()
126 {
127         $FSVERITY_PROG enable "$@"
128 }
129
130 _fsv_measure()
131 {
132         $FSVERITY_PROG measure "$@" | awk '{print $1}'
133 }
134
135 _fsv_sign()
136 {
137         $FSVERITY_PROG sign "$@"
138 }
139
140 # Generate a file, then enable verity on it.
141 _fsv_create_enable_file()
142 {
143         local file=$1
144         shift
145
146         head -c $((FSV_BLOCK_SIZE * 2)) /dev/zero > "$file"
147         _fsv_enable "$file" "$@"
148 }
149
150 _fsv_have_hash_algorithm()
151 {
152         local hash_alg=$1
153         local test_file=$2
154
155         rm -f $test_file
156         head -c 4096 /dev/zero > $test_file
157         if ! _fsv_enable --hash-alg=$hash_alg $test_file &>> $seqres.full; then
158                 # no kernel support
159                 return 1
160         fi
161         rm -f $test_file
162         return 0
163 }
164
165 #
166 # _fsv_scratch_corrupt_bytes - Write some bytes to a file, bypassing the filesystem
167 #
168 # Write the bytes sent on stdin to the given offset in the given file, but do so
169 # by writing directly to the extents on the block device, with the filesystem
170 # unmounted.  This can be used to corrupt a verity file for testing purposes,
171 # bypassing the restrictions imposed by the filesystem.
172 #
173 # The file is assumed to be located on $SCRATCH_DEV.
174 #
175 _fsv_scratch_corrupt_bytes()
176 {
177         local file=$1
178         local offset=$2
179         local lstart lend pstart pend
180         local dd_cmds=()
181         local cmd
182
183         sync    # Sync to avoid unwritten extents
184
185         cat > $tmp.bytes
186         local end=$(( offset + $(stat -c %s $tmp.bytes ) ))
187
188         # For each extent that intersects the requested range in order, add a
189         # command that writes the next part of the data to that extent.
190         while read -r lstart lend pstart pend; do
191                 lstart=$((lstart * 512))
192                 lend=$(((lend + 1) * 512))
193                 pstart=$((pstart * 512))
194                 pend=$(((pend + 1) * 512))
195
196                 if (( lend - lstart != pend - pstart )); then
197                         _fail "Logical and physical extent lengths differ for file '$file'"
198                 elif (( offset < lstart )); then
199                         _fail "Hole in file '$file' at byte $offset.  Next extent begins at byte $lstart"
200                 elif (( offset < lend )); then
201                         local len=$((lend - offset))
202                         local seek=$((pstart + (offset - lstart)))
203                         dd_cmds+=("head -c $len | dd of=$SCRATCH_DEV oflag=seek_bytes seek=$seek status=none")
204                         (( offset += len ))
205                 fi
206         done < <($XFS_IO_PROG -r -c "fiemap $offset $((end - offset))" "$file" \
207                  | _filter_xfs_io_fiemap)
208
209         if (( offset < end )); then
210                 _fail "Extents of file '$file' ended at byte $offset, but needed until $end"
211         fi
212
213         # Execute the commands to write the data
214         _scratch_unmount
215         for cmd in "${dd_cmds[@]}"; do
216                 eval "$cmd"
217         done < $tmp.bytes
218         sync    # Sync to flush the block device's pagecache
219         _scratch_mount
220 }
221
222 #
223 # _fsv_scratch_corrupt_merkle_tree - Corrupt a file's Merkle tree
224 #
225 # Like _fsv_scratch_corrupt_bytes(), but this corrupts the file's fs-verity
226 # Merkle tree.  The offset is given as a byte offset into the Merkle tree.
227 #
228 _fsv_scratch_corrupt_merkle_tree()
229 {
230         local file=$1
231         local offset=$2
232
233         case $FSTYP in
234         ext4|f2fs)
235                 # ext4 and f2fs store the Merkle tree after the file contents
236                 # itself, starting at the next 65536-byte aligned boundary.
237                 (( offset += ($(stat -c %s $file) + 65535) & ~65535 ))
238                 _fsv_scratch_corrupt_bytes $file $offset
239                 ;;
240         *)
241                 _fail "_fsv_scratch_corrupt_merkle_tree() unimplemented on $FSTYP"
242                 ;;
243         esac
244 }