From patchwork Mon Dec 10 22:21:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 10722723 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 40CAE1751 for ; Mon, 10 Dec 2018 22:25:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 32C8528610 for ; Mon, 10 Dec 2018 22:25:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 26EDF28715; Mon, 10 Dec 2018 22:25:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9E812296E2 for ; Mon, 10 Dec 2018 22:25:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729631AbeLJWZt (ORCPT ); Mon, 10 Dec 2018 17:25:49 -0500 Received: from mail.kernel.org ([198.145.29.99]:53502 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729482AbeLJWZt (ORCPT ); Mon, 10 Dec 2018 17:25:49 -0500 Received: from ebiggers-linuxstation.mtv.corp.google.com (unknown [104.132.1.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 9D08020855; Mon, 10 Dec 2018 22:25:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1544480747; bh=57Aa0ZmWRZ/IIjWCpLcn1ercHFRgZvfsStOXyk37NXM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VjexBu0YC1HEPJrIWdfLWfuA0/UOCrAe65IVqbmXbXLveSthNYTghJI3+IMwUZWQv Rxff21rzcCMKQdq8oR5vyn93eSb2u5nfC/kCJT91WGFZFf9emLBcF+9Bgw7rpMWuNk tdaPRna6BF0NaEX3Bhz/+qJOS2ls93LFJv38OxKw= From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-fscrypt@vger.kernel.org, "Theodore Y . Ts'o" , Jaegeuk Kim , Victor Hsieh Subject: [PATCH 1/7] common/verity: add common functions for testing fs-verity Date: Mon, 10 Dec 2018 14:21:36 -0800 Message-Id: <20181210222142.222342-2-ebiggers@kernel.org> X-Mailer: git-send-email 2.20.0.rc2.403.gdbc3b29805-goog In-Reply-To: <20181210222142.222342-1-ebiggers@kernel.org> References: <20181210222142.222342-1-ebiggers@kernel.org> MIME-Version: 1.0 Sender: fstests-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: fstests@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Eric Biggers Add common functions for setting up and testing fs-verity, a new feature for read-only file-based authenticity protection. fs-verity will be supported by ext4 and f2fs, and perhaps other filesystems later. Running the fs-verity tests requires: - A kernel with the fs-verity patches from https://git.kernel.org/pub/scm/linux/kernel/git/tytso/fscrypt.git/log/ (should be merged in 4.21) and configured with CONFIG_FS_VERITY. - The fsverity utility program, which can be installed from https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git/ - e2fsprogs v1.44.4-2 or later for ext4 tests, or f2fs-tools v1.11.0 or later for f2fs tests. See the file Documentation/filesystem/fsverity.rst in the kernel tree for more information about fs-verity. Signed-off-by: Eric Biggers --- common/config | 1 + common/verity | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 common/verity diff --git a/common/config b/common/config index a87cb4a2..b2160667 100644 --- a/common/config +++ b/common/config @@ -194,6 +194,7 @@ export GETCAP_PROG="$(type -P getcap)" export CHECKBASHISMS_PROG="$(type -P checkbashisms)" export XFS_INFO_PROG="$(type -P xfs_info)" export DUPEREMOVE_PROG="$(type -P duperemove)" +export FSVERITY_PROG="$(type -P fsverity)" # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled. # newer systems have udevadm command but older systems like RHEL5 don't. diff --git a/common/verity b/common/verity new file mode 100644 index 00000000..4da63b69 --- /dev/null +++ b/common/verity @@ -0,0 +1,198 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2018 Google LLC +# +# Functions for setting up and testing fs-verity + +FSV_BLOCK_SIZE=4096 + +_require_scratch_verity() +{ + _require_scratch + _require_command "$FSVERITY_PROG" fsverity + + if ! _scratch_mkfs_verity &>>$seqres.full; then + # ext4: need e2fsprogs v1.44.4-2 or later + # f2fs: need f2fs-tools v1.11.0 or later + _notrun "$FSTYP userspace tools don't support fs-verity" + fi + + # Try to mount the filesystem. If this fails, then the filesystem is + # unaware of the fs-verity feature. + if ! _try_scratch_mount &>>$seqres.full; then + _notrun "kernel doesn't know about $FSTYP verity feature" + fi + _scratch_unmount + + # The filesystem may be aware of fs-verity but have it disabled by + # CONFIG_FS_VERITY=n. Detect support via sysfs. + if [ ! -e /sys/fs/$FSTYP/features/verity ]; then + _notrun "kernel $FSTYP isn't configured with verity support" + fi + + # fs-verity with block_size != PAGE_SIZE isn't implemented yet. + # ("block_size" here refers to the fs-verity block size, not to the + # filesystem's block size.) + if [ "$(getconf PAGE_SIZE)" != $FSV_BLOCK_SIZE ]; then + _notrun "verity not yet supported for PAGE_SIZE != $FSV_BLOCK_SIZE" + fi +} + +_scratch_mkfs_verity() +{ + case $FSTYP in + ext4|f2fs) + _scratch_mkfs -O verity + ;; + *) + _notrun "No verity support for $FSTYP" + ;; + esac +} + +_scratch_mkfs_encrypted_verity() +{ + case $FSTYP in + ext4) + _scratch_mkfs -O encrypt,verity + ;; + f2fs) + # f2fs-tools as of v1.11.0 doesn't allow comma-separated + # features with -O. Instead -O must be supplied multiple times. + _scratch_mkfs -O encrypt -O verity + ;; + *) + _notrun "$FSTYP not supported in _scratch_mkfs_encrypted_verity" + ;; + esac +} + +_fsv_randstring() +{ + local nchars=$1 + + tr -d -C 0-9a-f < /dev/urandom | head -c "$nchars" +} + +_fsv_begin_subtest() +{ + local msg=$1 + + rm -rf "${SCRATCH_MNT:?}"/* + echo -e "\n# $msg" +} + +_fsv_setup() +{ + $FSVERITY_PROG setup "$@" | awk '/^File measurement: /{print $3}' +} + +_fsv_enable() +{ + $FSVERITY_PROG enable "$@" +} + +_fsv_measure() +{ + $FSVERITY_PROG measure "$@" | awk '{print $1}' +} + +# Generate a file with verity metadata, but don't actually enable verity yet +_fsv_create_setup_file() +{ + local file=$1 + + head -c $((FSV_BLOCK_SIZE * 2)) /dev/zero > "$file" + _fsv_setup "$file" +} + +# Generate a file with verity metadata, then enable verity +_fsv_create_enable_file() +{ + local file=$1 + + _fsv_create_setup_file "$file" + _fsv_enable "$file" +} + +# +# _fsv_corrupt_bytes - Write some bytes to a file, bypassing the filesystem +# +# Write the bytes sent on stdin to the given offset in the given file, but do so +# by writing directly to the extents on the block device, with the filesystem +# unmounted. This can be used to corrupt a verity file for testing purposes, +# bypassing the restrictions imposed by the filesystem. On ext4 and f2fs this +# can also write into the metadata region of a verity file. +# +# The file is assumed to be located on $SCRATCH_DEV. +# +_fsv_corrupt_bytes() +{ + local file=$1 + local offset=$2 + local lstarts=() # extent logical starts, in bytes + local pstarts=() # extent physical starts, in bytes + local lens=() # extent lengths, in bytes + local line + local cmd + local dd_cmds=() + local eidx=0 + + sync # Sync to avoid unwritten extents + + cat > $tmp.bytes + local end=$(( offset + $(stat -c %s $tmp.bytes ) )) + + # Get the list of extents that intersect the requested range + while read -r line; do \ + local fields=($line) + local lstart=${fields[0]} + local lend=${fields[1]} + local pstart=${fields[2]} + local pend=${fields[3]} + local llen=$((lend + 1 - lstart)) + local plen=$((pend + 1 - pstart)) + if (( llen != plen )); then + _fail "Logical and physical extent lengths differ! $line" + fi + lstarts+=( $((lstart * 512)) ) + pstarts+=( $((pstart * 512)) ) + lens+=( $((llen * 512)) ) + done < <($XFS_IO_PROG -r -c "fiemap $offset $((end - offset))" "$file" \ + | grep -E '^[[:space:]]+[0-9]+:' \ + | grep -v '\' \ + | sed -E 's/^[[:space:]]+[0-9]+://' \ + | tr '][.:' ' ') + + while (( offset < end )); do + # Find the next extent to write to + while true; do + if (( eidx >= ${#lstarts[@]} )); then + _fail "Extents ended before byte $offset" + fi + if (( offset < ${lstarts[$eidx]} )); then + _fail "Hole in file at byte $offset" + fi + local lend=$(( ${lstarts[$eidx]} + ${lens[$eidx]} )) + if (( offset < lend )); then + break + fi + (( eidx += 1 )) + done + # Add a command that writes to the next extent + local len=$((lend - offset)) + local seek=$(( offset + ${pstarts[$eidx]} - ${lstarts[$eidx]} )) + if (( len > end - offset )); then + len=$((end - offset)) + fi + dd_cmds+=("head -c $len | dd of=$SCRATCH_DEV oflag=seek_bytes seek=$seek status=none") + (( offset += len )) + done + + # Execute the commands to write the data + _scratch_unmount + for cmd in "${dd_cmds[@]}"; do + eval "$cmd" + done < $tmp.bytes + sync # Sync to flush the block device's pagecache + _scratch_mount +}