All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] Add filesystem-level encryption tests
@ 2016-12-05 19:21 Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 1/6] generic: add utilities for testing filesystem encryption Eric Biggers
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

This is the third version of the patch series to add some xfstests for
filesystem-level encryption.  The new tests are designed to run on any
filesystem that implements the "fscrypt" API, currently ext4 and f2fs.
(fscrypt support for ubifs is also under development, but it looks like
xfstests doesn't have ubifs support yet.)

The new tests are designed to complement, not replace, doing a full
xfstests run with encryption enabled, which for ext4 can currently be
done by using the test_dummy_encryption mount option.

Changes since v2:
	* Use filesystem-specific key prefix rather than generic one.
	* Use a new keyring for each test.
	* Add a test for restrictions on moving and linking files.
	* Make "cryptographic weaknesses" test compatible with f2fs.
	* For now, accept both the old and new versions of certain error
	  codes which are planned to be changed.

Changes since v1:
	* Drop fscrypt_util test program and use new xfs_io commands and
	  keyctl instead (xfs_io patch sent separately)
	* Updates to match xfstests coding style
	* Move validation of policy structure into its own test
	* Add test to detect some cryptographic weaknesses
	* Drop ioctl locking test

Eric Biggers (6):
  generic: add utilities for testing filesystem encryption
  generic: test setting and getting encryption policies
  generic: test validation of encryption policy structure
  generic: test encrypted file access
  generic: test enforcement of one encryption policy per tree
  generic: test for weaknesses in filesystem encryption

 common/config         |   2 +
 common/encrypt        | 137 ++++++++++++++++++++++++++++++++++++++++++
 tests/generic/400     | 132 ++++++++++++++++++++++++++++++++++++++++
 tests/generic/400.out |  43 +++++++++++++
 tests/generic/401     |  83 +++++++++++++++++++++++++
 tests/generic/401.out |  18 ++++++
 tests/generic/402     | 145 ++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/402.out |  13 ++++
 tests/generic/403     | 130 ++++++++++++++++++++++++++++++++++++++++
 tests/generic/403.out |  34 +++++++++++
 tests/generic/404     | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/404.out |   3 +
 tests/generic/group   |   5 ++
 13 files changed, 908 insertions(+)
 create mode 100644 common/encrypt
 create mode 100755 tests/generic/400
 create mode 100644 tests/generic/400.out
 create mode 100755 tests/generic/401
 create mode 100644 tests/generic/401.out
 create mode 100755 tests/generic/402
 create mode 100644 tests/generic/402.out
 create mode 100644 tests/generic/403
 create mode 100644 tests/generic/403.out
 create mode 100755 tests/generic/404
 create mode 100644 tests/generic/404.out

-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 1/6] generic: add utilities for testing filesystem encryption
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-13  5:41   ` Eryu Guan
  2016-12-05 19:21 ` [PATCH v3 2/6] generic: test setting and getting encryption policies Eric Biggers
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Add utility functions for testing filesystem-level encryption via the
common API currently supported by ext4 and f2fs, in development for
ubifs and planned for xfs.  Setting and getting encryption policies will
use new commands being added to xfs_io, while adding and removing
encryption keys will use keyctl.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/config  |   1 +
 common/encrypt | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)
 create mode 100644 common/encrypt

diff --git a/common/config b/common/config
index f0f08d2..3727ec0 100644
--- a/common/config
+++ b/common/config
@@ -202,6 +202,7 @@ export DEBUGFS_PROG="`set_prog_path debugfs`"
 export UUIDGEN_PROG="`set_prog_path uuidgen`"
 export GETRICHACL_PROG="`set_prog_path getrichacl`"
 export SETRICHACL_PROG="`set_prog_path setrichacl`"
+export KEYCTL_PROG="`set_prog_path keyctl`"
 
 # 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/encrypt b/common/encrypt
new file mode 100644
index 0000000..e18068e
--- /dev/null
+++ b/common/encrypt
@@ -0,0 +1,137 @@
+#-----------------------------------------------------------------------
+#
+# Common functions for testing filesystem-level encryption
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+
+_require_encryption()
+{
+	# The 'test_dummy_encryption' mount option interferes with trying to use
+	# encryption for real, even if we are just trying to get/set policies
+	# and never put any keys in the keyring.  So skip the real encryption
+	# tests if the 'test_dummy_encryption' mount option was specified.
+	if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
+		_notrun "Dummy encryption is on; skipping real encryption tests"
+	fi
+
+	# Make a filesystem on the scratch device with the encryption feature
+	# enabled.  If this fails then probably the userspace tools (e.g.
+	# e2fsprogs or f2fs-tools) are too old to understand encryption.
+	if ! _scratch_mkfs_encrypted &>>$seqres.full; then
+		_notrun "$FSTYP userspace tools do not support encryption"
+	fi
+
+	# Try to mount the filesystem.  If this fails then either the kernel
+	# isn't aware of encryption, or the mkfs options were not compatible
+	# with encryption (e.g. ext4 with block size != PAGE_SIZE).
+	if ! _scratch_mount &>>$seqres.full; then
+		_notrun "kernel is unaware of $FSTYP encryption feature, " \
+			"or mkfs options are not compatible with encryption"
+	fi
+
+	# The kernel may be aware of encryption without supporting it.  For
+	# example, for ext4 this is the case with kernels configured with
+	# CONFIG_EXT4_FS_ENCRYPTION=n.  Detect support for encryption by trying
+	# to set an encryption policy.  (For ext4 we could instead check for the
+	# presence of /sys/fs/ext4/features/encryption, but this is broken on
+	# some older kernels and is ext4-specific anyway.)
+	mkdir $SCRATCH_MNT/tmpdir
+	if $XFS_IO_PROG -c set_encpolicy $SCRATCH_MNT/tmpdir \
+		2>&1 >>$seqres.full | \
+		egrep -q 'Inappropriate ioctl for device|Operation not supported'
+	then
+		_notrun "kernel does not support $FSTYP encryption"
+	fi
+	rmdir $SCRATCH_MNT/tmpdir
+	_scratch_unmount
+}
+
+_scratch_mkfs_encrypted()
+{
+	_scratch_mkfs -O encrypt
+}
+
+# Give the invoking shell a new session keyring.  This makes any keys we add to
+# the session keyring scoped to the lifetime of the test script.
+_new_session_keyring()
+{
+	$KEYCTL_PROG new_session >>$seqres.full
+}
+
+#
+# Generate a random encryption key, add it to the session keyring, and print out
+# the resulting key descriptor (example: "8bf798e1a494e1ec").  Requires the
+# keyctl program.  It's assumed the caller has already set up a test-scoped
+# session keyring using _new_session_keyring.
+#
+_generate_encryption_key()
+{
+	# Generate a key descriptor (16 character hex string)
+	local keydesc=""
+	for ((i = 0; i < 8; i++)); do
+		keydesc="${keydesc}$(printf "%02x" $(( $RANDOM % 256 )))"
+	done
+
+	# Generate the actual encryption key (64 bytes)
+	local raw=""
+	for ((i = 0; i < 64; i++)); do
+		raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))"
+	done
+
+	#
+	# Add the key to the session keyring.  The required structure is:
+	#
+	#	#define FS_MAX_KEY_SIZE 64
+	#	struct fscrypt_key {
+	#		u32 mode;
+	#		u8 raw[FS_MAX_KEY_SIZE];
+	#		u32 size;
+	#	} __packed;
+	#
+	# The kernel ignores 'mode' but requires that 'size' be 64.
+	#
+	# Keys are named $FSTYP:KEYDESC where KEYDESC is the 16-character key
+	# descriptor hex string.  Newer kernels (ext4 4.8 and later, f2fs 4.6
+	# and later) also allow the common key prefix "fscrypt:" in addition to
+	# their filesystem-specific key prefix ("ext4:", "f2fs:").  It would be
+	# nice to use the common key prefix, but for now use the filesystem-
+	# specific prefix to make it possible to test older kernels...
+	#
+	local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
+			   cut -f2 -d' ' | cut -c1 )
+	if (( big_endian )); then
+		local mode='\x00\x00\x00\x00'
+		local size='\x00\x00\x00\x40'
+	else
+		local mode='\x00\x00\x00\x00'
+		local size='\x40\x00\x00\x00'
+	fi
+	echo -n -e "${mode}${raw}${size}" |
+		$KEYCTL_PROG padd logon $FSTYP:$keydesc @s >>$seqres.full
+	echo $keydesc
+}
+
+# Unlink an encryption key from the session keyring, given its key descriptor.
+_unlink_encryption_key()
+{
+	local keydesc=$1
+	local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
+	$KEYCTL_PROG unlink $keyid >>$seqres.full
+}
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 2/6] generic: test setting and getting encryption policies
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 1/6] generic: add utilities for testing filesystem encryption Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-13  5:59   ` Eryu Guan
  2016-12-05 19:21 ` [PATCH v3 3/6] generic: test validation of encryption policy structure Eric Biggers
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Several kernel bugs were recently fixed regarding the constraints for
setting encryption policies.  Add tests for these cases and a few more.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/400     | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/400.out |  43 ++++++++++++++++
 tests/generic/group   |   1 +
 3 files changed, 176 insertions(+)
 create mode 100755 tests/generic/400
 create mode 100644 tests/generic/400.out

diff --git a/tests/generic/400 b/tests/generic/400
new file mode 100755
index 0000000..6bcc65d
--- /dev/null
+++ b/tests/generic/400
@@ -0,0 +1,132 @@
+#! /bin/bash
+# FS QA Test generic/400
+#
+# Test setting and getting encryption policies.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4 f2fs
+_supported_os Linux
+_require_xfs_io_command "get_encpolicy"
+_require_xfs_io_command "set_encpolicy"
+_require_scratch
+_require_encryption
+_require_user
+
+_scratch_mkfs_encrypted >> $seqres.full
+_scratch_mount
+cd $SCRATCH_MNT
+
+check_no_policy()
+{
+	# When a file is unencrypted, FS_IOC_GET_ENCRYPTION_POLICY currently
+	# fails with ENOENT on ext4 but with ENODATA on f2fs.  TODO: it's
+	# planned to consistently use ENODATA.  For now this test accepts both.
+	$XFS_IO_PROG -c "get_encpolicy" $1 |&
+		sed -e 's/No such file or directory/No data available/'
+}
+
+# Should be able to set an encryption policy on an empty directory
+echo -e "\n*** Setting encryption policy on empty directory ***"
+mkdir empty_dir
+check_no_policy empty_dir
+$XFS_IO_PROG -c "set_encpolicy 0000111122223333" empty_dir
+$XFS_IO_PROG -c "get_encpolicy" empty_dir
+
+# Should be able to set the same policy again, but not a different one.
+# TODO: the error code for "already has a different policy" is planned to switch
+# from EINVAL to EEXIST.  For now this test accepts both.
+echo -e "\n*** Setting encryption policy again ***"
+$XFS_IO_PROG -c "set_encpolicy 0000111122223333" empty_dir
+$XFS_IO_PROG -c "get_encpolicy" empty_dir
+$XFS_IO_PROG -c "set_encpolicy 4444555566667777" empty_dir |& \
+	sed -e 's/Invalid argument/File exists/'
+$XFS_IO_PROG -c "get_encpolicy" empty_dir
+
+# Should *not* be able to set an encryption policy on a nonempty directory
+echo -e "\n*** Setting encryption policy on nonempty directory ***"
+mkdir nonempty_dir
+touch nonempty_dir/file
+$XFS_IO_PROG -c "set_encpolicy" nonempty_dir
+check_no_policy nonempty_dir
+
+# Should *not* be able to set an encryption policy on a nondirectory file, even
+# an empty one.  Regression test for 002ced4be642: "fscrypto: only allow setting
+# encryption policy on directories".
+# TODO: the error code for "not a directory" is planned to switch from EINVAL to
+# ENOTDIR.  For now this test accepts both.
+echo -e "\n*** Setting encryption policy on nondirectory ***"
+touch nondirectory
+$XFS_IO_PROG -c "set_encpolicy" nondirectory |& \
+	sed -e 's/Invalid argument/Not a directory/'
+check_no_policy nondirectory
+
+# Should *not* be able to set an encryption policy on another user's directory.
+# Regression test for 163ae1c6ad62: "fscrypto: add authorization check for
+# setting encryption policy".
+echo -e "\n*** Setting encryption policy on another user's directory ***"
+mkdir unauthorized_dir
+su $qa_user -c "$XFS_IO_PROG -c \"set_encpolicy\" unauthorized_dir"
+check_no_policy unauthorized_dir
+
+# Should *not* be able to set an encryption policy on a directory on a
+# filesystem mounted readonly.  Regression test for ba63f23d69a3: "fscrypto:
+# require write access to mount to set encryption policy".  Test both a regular
+# readonly filesystem and a readonly bind mount of a read-write filesystem.
+echo -e "\n*** Setting encryption policy on readonly filesystem ***"
+mkdir ro_dir ro_bind_mnt
+_scratch_remount ro
+$XFS_IO_PROG -c "set_encpolicy" ro_dir
+check_no_policy ro_dir
+_scratch_remount rw
+mount --bind $SCRATCH_MNT ro_bind_mnt
+mount -o remount,ro ro_bind_mnt
+$XFS_IO_PROG -c "set_encpolicy" ro_bind_mnt/ro_dir
+check_no_policy ro_bind_mnt/ro_dir
+umount ro_bind_mnt
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/400.out b/tests/generic/400.out
new file mode 100644
index 0000000..d187841
--- /dev/null
+++ b/tests/generic/400.out
@@ -0,0 +1,43 @@
+QA output created by 400
+
+*** Setting encryption policy on empty directory ***
+empty_dir: failed to get encryption policy: No data available
+Encryption policy for empty_dir:
+	Policy version: 0
+	Master key descriptor: 0000111122223333
+	Contents encryption mode: 1 (AES-256-XTS)
+	Filenames encryption mode: 4 (AES-256-CTS)
+	Flags: 0x02
+
+*** Setting encryption policy again ***
+Encryption policy for empty_dir:
+	Policy version: 0
+	Master key descriptor: 0000111122223333
+	Contents encryption mode: 1 (AES-256-XTS)
+	Filenames encryption mode: 4 (AES-256-CTS)
+	Flags: 0x02
+empty_dir: failed to set encryption policy: File exists
+Encryption policy for empty_dir:
+	Policy version: 0
+	Master key descriptor: 0000111122223333
+	Contents encryption mode: 1 (AES-256-XTS)
+	Filenames encryption mode: 4 (AES-256-CTS)
+	Flags: 0x02
+
+*** Setting encryption policy on nonempty directory ***
+nonempty_dir: failed to set encryption policy: Directory not empty
+nonempty_dir: failed to get encryption policy: No data available
+
+*** Setting encryption policy on nondirectory ***
+nondirectory: failed to set encryption policy: Not a directory
+nondirectory: failed to get encryption policy: No data available
+
+*** Setting encryption policy on another user's directory ***
+unauthorized_dir: failed to set encryption policy: Permission denied
+unauthorized_dir: failed to get encryption policy: No data available
+
+*** Setting encryption policy on readonly filesystem ***
+ro_dir: failed to set encryption policy: Read-only file system
+ro_dir: failed to get encryption policy: No data available
+ro_bind_mnt/ro_dir: failed to set encryption policy: Read-only file system
+ro_bind_mnt/ro_dir: failed to get encryption policy: No data available
diff --git a/tests/generic/group b/tests/generic/group
index f4af986..e6619ca 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -396,3 +396,4 @@
 391 auto quick rw
 392 auto quick metadata
 393 auto quick rw
+400 auto quick encrypt
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 3/6] generic: test validation of encryption policy structure
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 1/6] generic: add utilities for testing filesystem encryption Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 2/6] generic: test setting and getting encryption policies Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 4/6] generic: test encrypted file access Eric Biggers
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Add an xfstest which verifies the kernel performs basic validation of
the encryption policy structure.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/401     | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/401.out | 18 +++++++++++
 tests/generic/group   |  1 +
 3 files changed, 102 insertions(+)
 create mode 100755 tests/generic/401
 create mode 100644 tests/generic/401.out

diff --git a/tests/generic/401 b/tests/generic/401
new file mode 100755
index 0000000..553c547
--- /dev/null
+++ b/tests/generic/401
@@ -0,0 +1,83 @@
+#! /bin/bash
+# FS QA Test generic/401
+#
+# Test that FS_IOC_SET_ENCRYPTION_POLICY correctly validates the fscrypt_policy
+# structure that userspace passes to it.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4 f2fs
+_supported_os Linux
+_require_xfs_io_command "set_encpolicy"
+_require_scratch
+_require_encryption
+
+_scratch_mkfs_encrypted >> $seqres.full
+_scratch_mount
+mkdir $SCRATCH_MNT/dir
+cd $SCRATCH_MNT
+
+echo -e "\n*** Invalid contents encryption mode ***"
+$XFS_IO_PROG -c "set_encpolicy -c 0xFF" dir
+
+echo -e "\n*** Invalid filenames encryption mode ***"
+$XFS_IO_PROG -c "set_encpolicy -n 0xFF" dir
+
+echo -e "\n*** Invalid flags ***"
+$XFS_IO_PROG -c "set_encpolicy -f 0xFF" dir
+
+echo -e "\n*** Invalid policy version ***"
+$XFS_IO_PROG -c "set_encpolicy -v 0xFF" dir
+
+# Currently, the only supported combination of modes is AES-256-XTS for contents
+# and AES-256-CTS for filenames.  Nothing else should be accepted.
+echo -e "\n*** Invalid combinations of modes ***"
+$XFS_IO_PROG -c "set_encpolicy -c AES-256-CTS -n AES-256-CTS" dir
+$XFS_IO_PROG -c "set_encpolicy -c AES-256-CTS -n AES-256-XTS" dir
+$XFS_IO_PROG -c "set_encpolicy -c AES-256-XTS -n AES-256-XTS" dir
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/401.out b/tests/generic/401.out
new file mode 100644
index 0000000..eb6142e
--- /dev/null
+++ b/tests/generic/401.out
@@ -0,0 +1,18 @@
+QA output created by 401
+
+*** Invalid contents encryption mode ***
+dir: failed to set encryption policy: Invalid argument
+
+*** Invalid filenames encryption mode ***
+dir: failed to set encryption policy: Invalid argument
+
+*** Invalid flags ***
+dir: failed to set encryption policy: Invalid argument
+
+*** Invalid policy version ***
+dir: failed to set encryption policy: Invalid argument
+
+*** Invalid combinations of modes ***
+dir: failed to set encryption policy: Invalid argument
+dir: failed to set encryption policy: Invalid argument
+dir: failed to set encryption policy: Invalid argument
diff --git a/tests/generic/group b/tests/generic/group
index e6619ca..a455c29 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -397,3 +397,4 @@
 392 auto quick metadata
 393 auto quick rw
 400 auto quick encrypt
+401 auto quick encrypt
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 4/6] generic: test encrypted file access
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
                   ` (2 preceding siblings ...)
  2016-12-05 19:21 ` [PATCH v3 3/6] generic: test validation of encryption policy structure Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-05 19:21 ` [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree Eric Biggers
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Test accessing encrypted files and directories, both with and without
the encryption key.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/402     | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/402.out |  13 +++++
 tests/generic/group   |   1 +
 3 files changed, 159 insertions(+)
 create mode 100755 tests/generic/402
 create mode 100644 tests/generic/402.out

diff --git a/tests/generic/402 b/tests/generic/402
new file mode 100755
index 0000000..f644196
--- /dev/null
+++ b/tests/generic/402
@@ -0,0 +1,145 @@
+#! /bin/bash
+# FS QA Test generic/402
+#
+# Test accessing encrypted files and directories, both with and without the
+# encryption key.  Access with the encryption key is more of a sanity check and
+# is not intended to fully test all the encrypted I/O paths; to do that you'd
+# need to run all the xfstests with encryption enabled.  Access without the
+# encryption key, on the other hand, should result in some particular behaviors.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4 f2fs
+_supported_os Linux
+_require_xfs_io_command "set_encpolicy"
+_require_scratch
+_require_command "$KEYCTL_PROG" keyctl
+_require_encryption
+
+_new_session_keyring
+
+_scratch_mkfs_encrypted >> $seqres.full
+_scratch_mount
+
+mkdir $SCRATCH_MNT/edir $SCRATCH_MNT/ref_dir
+keydesc=$(_generate_encryption_key)
+$XFS_IO_PROG -c "set_encpolicy $keydesc" $SCRATCH_MNT/edir
+for dir in $SCRATCH_MNT/edir $SCRATCH_MNT/ref_dir; do
+	touch $dir/empty > /dev/null
+	$XFS_IO_PROG -t -f -c "pwrite 0 4k" $dir/a > /dev/null
+	$XFS_IO_PROG -t -f -c "pwrite 0 33k" $dir/abcdefghijklmnopqrstuvwxyz > /dev/null
+	maxname=$(yes | head -255 | tr -d '\n') # 255 character filename
+	$XFS_IO_PROG -t -f -c "pwrite 0 1k" $dir/$maxname > /dev/null
+	ln -s a $dir/symlink
+	ln -s abcdefghijklmnopqrstuvwxyz $dir/symlink2
+	ln -s $maxname $dir/symlink3
+	mkdir $dir/subdir
+	mkdir $dir/subdir/subsubdir
+done
+# Diff encrypted directory with unencrypted reference directory
+diff -r $SCRATCH_MNT/edir $SCRATCH_MNT/ref_dir
+# Cycle mount and diff again
+_scratch_cycle_mount
+diff -r $SCRATCH_MNT/edir $SCRATCH_MNT/ref_dir
+
+#
+# Now try accessing the files without the encryption key.  It should still be
+# possible to list the directory and remove files.  But filenames should be
+# encrypted, and it should not be possible to read regular files or to create
+# new files or subdirectories.
+#
+# Note that we cannot simply use ls -R to verify the files because the encrypted
+# filenames are unpredictable.  By design, the key used to encrypt a directory's
+# filenames is derived from the master key (the key in the keyring) and a nonce
+# generated by the kernel.  Hence, the encrypted filenames will be different
+# every time this test is run, even if we were to put a fixed key into the
+# keyring instead of a random one.  The same applies to symlink targets.
+#
+# TODO: there are some inconsistencies in which error codes are returned on
+# different kernel versions and filesystems when trying to create a file or
+# subdirectory without access to the parent directory's encryption key.  It's
+# planned to consistently use ENOKEY, but for now make this test accept multiple
+# error codes...
+#
+
+filter_create_errors()
+{
+	sed -e 's/No such file or directory/Required key not available/' \
+	    -e 's/Permission denied/Required key not available/' \
+	    -e 's/Operation not permitted/Required key not available/'
+}
+
+_unlink_encryption_key $keydesc
+_scratch_cycle_mount
+
+# Check that unencrypted names aren't there
+stat $SCRATCH_MNT/edir/empty |& _filter_scratch
+stat $SCRATCH_MNT/edir/symlink |& _filter_scratch
+
+# Check that the correct numbers of files and subdirectories are there
+ls $SCRATCH_MNT/edir | wc -l
+find $SCRATCH_MNT/edir -mindepth 2 -maxdepth 2 -type d | wc -l
+
+# Try to read a nondirectory file (should fail with ENOKEY)
+md5sum $(find $SCRATCH_MNT/edir -maxdepth 1 -type f | head -1) |& \
+		cut -d ' ' -f3-
+
+# Try to create new files, directories, and symlinks in the encrypted directory,
+# both with and without using correctly base-64 encoded filenames.  These should
+# all fail with ENOKEY.
+$XFS_IO_PROG -f $SCRATCH_MNT/edir/newfile |& filter_create_errors | _filter_scratch
+$XFS_IO_PROG -f $SCRATCH_MNT/edir/0123456789abcdef |& filter_create_errors | _filter_scratch
+mkdir $SCRATCH_MNT/edir/newdir |& filter_create_errors | _filter_scratch
+mkdir $SCRATCH_MNT/edir/0123456789abcdef |& filter_create_errors | _filter_scratch
+ln -s foo $SCRATCH_MNT/edir/newlink |& filter_create_errors | _filter_scratch
+ln -s foo $SCRATCH_MNT/edir/0123456789abcdef |& filter_create_errors | _filter_scratch
+
+# Delete the encrypted directory (should succeed)
+rm -r $SCRATCH_MNT/edir
+stat $SCRATCH_MNT/edir |& _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/402.out b/tests/generic/402.out
new file mode 100644
index 0000000..8573474
--- /dev/null
+++ b/tests/generic/402.out
@@ -0,0 +1,13 @@
+QA output created by 402
+stat: cannot stat 'SCRATCH_MNT/edir/empty': No such file or directory
+stat: cannot stat 'SCRATCH_MNT/edir/symlink': No such file or directory
+8
+1
+Required key not available
+SCRATCH_MNT/edir/newfile: Required key not available
+SCRATCH_MNT/edir/0123456789abcdef: Required key not available
+mkdir: cannot create directory 'SCRATCH_MNT/edir/newdir': Required key not available
+mkdir: cannot create directory 'SCRATCH_MNT/edir/0123456789abcdef': Required key not available
+ln: failed to create symbolic link 'SCRATCH_MNT/edir/newlink': Required key not available
+ln: failed to create symbolic link 'SCRATCH_MNT/edir/0123456789abcdef': Required key not available
+stat: cannot stat 'SCRATCH_MNT/edir': No such file or directory
diff --git a/tests/generic/group b/tests/generic/group
index a455c29..e218380 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -398,3 +398,4 @@
 393 auto quick rw
 400 auto quick encrypt
 401 auto quick encrypt
+402 auto quick encrypt
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
                   ` (3 preceding siblings ...)
  2016-12-05 19:21 ` [PATCH v3 4/6] generic: test encrypted file access Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-13  6:07   ` Eryu Guan
  2016-12-05 19:21 ` [PATCH v3 6/6] generic: test for weaknesses in filesystem encryption Eric Biggers
  2016-12-13  6:12 ` [PATCH v3 0/6] Add filesystem-level encryption tests Eryu Guan
  6 siblings, 1 reply; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Add an xfstest which partially verifies that the filesystem enforces
that all files in an encrypted directory tree use the same encryption
policy.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 tests/generic/403     | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/403.out |  34 +++++++++++++
 tests/generic/group   |   1 +
 3 files changed, 165 insertions(+)
 create mode 100644 tests/generic/403
 create mode 100644 tests/generic/403.out

diff --git a/tests/generic/403 b/tests/generic/403
new file mode 100644
index 0000000..77427b8
--- /dev/null
+++ b/tests/generic/403
@@ -0,0 +1,130 @@
+#! /bin/bash
+# FS QA Test generic/403
+#
+# Filesystem encryption is designed to enforce that a consistent encryption
+# policy is used within a given encrypted directory tree and that an encrypted
+# directory tree does not contain any unencrypted files.  This test verifies
+# that filesystem operations that would violate this constraint fail with EPERM.
+# This does not yet test enforcement of this constraint on lookup, which is
+# needed to detect offline changes.
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+. ./common/renameat2
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4 f2fs
+_supported_os Linux
+_require_xfs_io_command "set_encpolicy"
+_require_scratch
+_require_encryption
+_requires_renameat2
+
+_new_session_keyring
+_scratch_mkfs_encrypted >> $seqres.full
+_scratch_mount
+
+# Set up two encrypted directories, with different encryption policies,
+# and one unencrypted directory.
+edir1=$SCRATCH_MNT/edir1
+edir2=$SCRATCH_MNT/edir2
+udir=$SCRATCH_MNT/udir
+mkdir $edir1 $edir2 $udir
+keydesc1=$(_generate_encryption_key)
+keydesc2=$(_generate_encryption_key)
+$XFS_IO_PROG -c "set_encpolicy $keydesc1" $edir1
+$XFS_IO_PROG -c "set_encpolicy $keydesc2" $edir2
+touch $edir1/efile1
+touch $edir2/efile2
+touch $udir/ufile
+
+echo -e "\n*** Link encrypted <= encrypted ***"
+ln $edir1/efile1 $edir2/efile1 |& _filter_scratch
+
+echo -e "\n*** Rename encrypted => encrypted ***"
+mv $edir1/efile1 $edir2/efile1 |& _filter_scratch
+
+echo -e "\n*** Exchange encrypted <=> encrypted ***"
+src/renameat2 -x $edir1/efile1 $edir2/efile2 |& _filter_scratch
+
+
+echo -e "\n\n*** Link unencrypted <= encrypted ***"
+ln $udir/ufile $edir1/ufile |& _filter_scratch
+
+echo -e "\n*** Rename unencrypted => encrypted ***"
+mv $udir/ufile $edir1/ufile |& _filter_scratch
+
+echo -e "\n*** Exchange unencrypted <=> encrypted ***"
+src/renameat2 -x $udir/ufile $edir1/efile1 |& _filter_scratch
+
+
+echo -e "\n\n*** Link encrypted <= unencrypted ***"
+ln -v $edir1/efile1 $udir/efile1 |& _filter_scratch # should succeed
+rm $udir/efile1 # undo
+
+echo -e "\n*** Rename encrypted => unencrypted ***"
+mv -v $edir1/efile1 $udir/efile1 |& _filter_scratch # should succeed
+mv $udir/efile1 $edir1/efile1 # undo
+
+echo -e "\n*** Exchange encrypted <=> unencrypted ***"
+src/renameat2 -x $edir1/efile1 $udir/ufile |& _filter_scratch
+
+# Now test the cases where we don't have access to the encryption keys.
+
+_unlink_encryption_key $keydesc1
+_unlink_encryption_key $keydesc2
+_scratch_cycle_mount
+efile1=$(find $edir1 -type f)
+efile2=$(find $edir2 -type f)
+echo
+
+# TODO: this currently succeeds.  It should fail.  Fix this kernel-side.
+#echo -e "\n*** Exchange encrypted <=> encrypted without key ***"
+#src/renameat2 -x $efile1 $efile2
+
+echo -e "\n*** Exchange encrypted <=> unencrypted without key ***"
+src/renameat2 -x $efile1 $udir/ufile
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/403.out b/tests/generic/403.out
new file mode 100644
index 0000000..27ed8cb
--- /dev/null
+++ b/tests/generic/403.out
@@ -0,0 +1,34 @@
+QA output created by 403
+
+*** Link encrypted <= encrypted ***
+ln: failed to create hard link 'SCRATCH_MNT/edir2/efile1' => 'SCRATCH_MNT/edir1/efile1': Operation not permitted
+
+*** Rename encrypted => encrypted ***
+mv: cannot move 'SCRATCH_MNT/edir1/efile1' to 'SCRATCH_MNT/edir2/efile1': Operation not permitted
+
+*** Exchange encrypted <=> encrypted ***
+Operation not permitted
+
+
+*** Link unencrypted <= encrypted ***
+ln: failed to create hard link 'SCRATCH_MNT/edir1/ufile' => 'SCRATCH_MNT/udir/ufile': Operation not permitted
+
+*** Rename unencrypted => encrypted ***
+mv: cannot move 'SCRATCH_MNT/udir/ufile' to 'SCRATCH_MNT/edir1/ufile': Operation not permitted
+
+*** Exchange unencrypted <=> encrypted ***
+Operation not permitted
+
+
+*** Link encrypted <= unencrypted ***
+'SCRATCH_MNT/udir/efile1' => 'SCRATCH_MNT/edir1/efile1'
+
+*** Rename encrypted => unencrypted ***
+'SCRATCH_MNT/edir1/efile1' -> 'SCRATCH_MNT/udir/efile1'
+
+*** Exchange encrypted <=> unencrypted ***
+Operation not permitted
+
+
+*** Exchange encrypted <=> unencrypted without key ***
+Operation not permitted
diff --git a/tests/generic/group b/tests/generic/group
index e218380..a0d6e84 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -399,3 +399,4 @@
 400 auto quick encrypt
 401 auto quick encrypt
 402 auto quick encrypt
+403 auto quick encrypt
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 6/6] generic: test for weaknesses in filesystem encryption
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
                   ` (4 preceding siblings ...)
  2016-12-05 19:21 ` [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree Eric Biggers
@ 2016-12-05 19:21 ` Eric Biggers
  2016-12-13  6:12 ` [PATCH v3 0/6] Add filesystem-level encryption tests Eryu Guan
  6 siblings, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2016-12-05 19:21 UTC (permalink / raw)
  To: fstests
  Cc: Theodore Ts'o, Jaegeuk Kim, Richard Weinberger, David Gstir,
	Michael Halcrow, Eric Biggers

Add an xfstest which can detect some basic crypto mistakes that would
reduce the confidentiality guarantee provided by filesystem encryption.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 common/config         |   1 +
 tests/generic/404     | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/404.out |   3 +
 tests/generic/group   |   1 +
 4 files changed, 168 insertions(+)
 create mode 100755 tests/generic/404
 create mode 100644 tests/generic/404.out

diff --git a/common/config b/common/config
index 3727ec0..6cce7ce 100644
--- a/common/config
+++ b/common/config
@@ -203,6 +203,7 @@ export UUIDGEN_PROG="`set_prog_path uuidgen`"
 export GETRICHACL_PROG="`set_prog_path getrichacl`"
 export SETRICHACL_PROG="`set_prog_path setrichacl`"
 export KEYCTL_PROG="`set_prog_path keyctl`"
+export XZ_PROG="`set_prog_path xz`"
 
 # 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/tests/generic/404 b/tests/generic/404
new file mode 100755
index 0000000..7b0e842
--- /dev/null
+++ b/tests/generic/404
@@ -0,0 +1,163 @@
+#! /bin/bash
+# FS QA Test generic/404
+#
+# Check for weaknesses in filesystem encryption involving the same ciphertext
+# being repeated.  For file contents, we fill a small filesystem with large
+# files of 0's and verify the filesystem is incompressible.  For filenames, we
+# create an identical symlink in two different directories and verify the
+# ciphertext filenames and symlink targets are different.
+#
+# This test can detect some basic cryptographic mistakes such as nonce reuse
+# (across files), initialization vector reuse (across blocks), or data somehow
+# being left in plaintext by accident.  For example, it detects the
+# initialization vector reuse bug fixed in commit 02fc59a0d28f ("f2fs/crypto:
+# fix xts_tweak initialization").
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1	# failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+	cd /
+	rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs ext4 f2fs
+_supported_os Linux
+_require_xfs_io_command "set_encpolicy"
+_require_scratch
+_require_command "$XZ_PROG" xz
+_require_command "$KEYCTL_PROG" keyctl
+_require_encryption
+
+_new_session_keyring
+
+# Set up a small filesystem containing an encrypted directory.  64 MB is enough
+# for both ext4 and f2fs.  (f2fs doesn't support a 32 MB filesystem.)
+fs_size_in_mb=64
+fs_size=$((fs_size_in_mb * 1024 * 1024))
+dd if=/dev/zero of=$SCRATCH_DEV bs=$((1024 * 1024)) \
+	count=$fs_size_in_mb &>> $seqres.full
+MKFS_OPTIONS="$MKFS_OPTIONS -O encrypt" \
+	_scratch_mkfs_sized $fs_size &>> $seqres.full
+_scratch_mount
+
+keydesc=$(_generate_encryption_key)
+mkdir $SCRATCH_MNT/encrypted_dir
+$XFS_IO_PROG -c "set_encpolicy $keydesc" $SCRATCH_MNT/encrypted_dir
+
+# Create the "same" symlink in two different directories.
+# Later we'll check both the name and target of the symlink.
+mkdir $SCRATCH_MNT/encrypted_dir/subdir1
+mkdir $SCRATCH_MNT/encrypted_dir/subdir2
+ln -s symlink_target $SCRATCH_MNT/encrypted_dir/subdir1/symlink
+ln -s symlink_target $SCRATCH_MNT/encrypted_dir/subdir2/symlink
+
+#
+# Write files of 1 MB of all the same byte until we hit ENOSPC.  Note that we
+# must not create sparse files, since the contents of sparse files are not
+# stored on-disk.  Also, we create multiple files rather than one big file
+# because we want to test for reuse of per-file keys.
+#
+total_file_size=0
+i=1
+while true; do
+	file=$SCRATCH_MNT/encrypted_dir/file$i
+	if ! xfs_io -f $file -c 'pwrite 0 1M' &> $tmp.out; then
+		if ! grep -q 'No space left on device' $tmp.out; then
+			echo "FAIL: unexpected pwrite failure"
+			cat $tmp.out
+		elif [ -e $file ]; then
+			total_file_size=$((total_file_size + $(stat -c %s $file)))
+		fi
+		break
+	fi
+	total_file_size=$((total_file_size + $(stat -c %s $file)))
+	i=$((i + 1))
+	if [ $i -gt $fs_size_in_mb ]; then
+		echo "FAIL: filesystem never filled up!"
+		break
+	fi
+done
+
+# We shouldn't have been able to write more data than we had space for.
+if (( $total_file_size > $fs_size )); then
+	echo "FAIL: wrote $total_file_size bytes but should have only" \
+		"had space for $fs_size bytes at most"
+fi
+
+#
+# Unmount the filesystem and compute its compressed size.  It must be no smaller
+# than the amount of data that was written; otherwise there was a compromise in
+# the confidentiality of the data.  False positives should not be possible
+# because filesystem metadata will also contribute to the compressed size.
+#
+# Note: it's important to use a strong compressor such as xz which can detect
+# redundancy across most or all of the filesystem.  We run xz with a 64 MB
+# sliding window but use some custom settings to make it faster and use less
+# memory than the '-9' preset.  The memory needed with our settings will be
+# 64 * 6.5 = 416 MB; see xz(1).
+#
+_unlink_encryption_key $keydesc
+_scratch_unmount
+fs_compressed_size=$(head -c $fs_size $SCRATCH_DEV | \
+	xz --lzma2=dict=64M,mf=hc4,mode=fast,nice=16 | \
+	wc -c)
+
+if (( $fs_compressed_size < $total_file_size )); then
+	echo "FAIL: filesystem was compressible" \
+		"($total_file_size bytes => $fs_compressed_size bytes)"
+else
+	echo "PASS: ciphertexts were not repeated for contents"
+fi
+
+# Verify that encrypted filenames and symlink targets were not reused.  Note
+# that since the ciphertexts should be unpredictable, we cannot simply include
+# the expected names in the expected output file.
+_scratch_mount
+find $SCRATCH_MNT/encrypted_dir -type l | wc -l
+link1=$(find $SCRATCH_MNT/encrypted_dir -type l | head -1)
+link2=$(find $SCRATCH_MNT/encrypted_dir -type l | tail -1)
+[ $(basename $link1) = $(basename $link2) ] && \
+	echo "Encrypted filenames were reused!"
+[ $(readlink $link1) = $(readlink $link2) ] && \
+	echo "Encrypted symlink targets were reused!"
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/404.out b/tests/generic/404.out
new file mode 100644
index 0000000..220edb4
--- /dev/null
+++ b/tests/generic/404.out
@@ -0,0 +1,3 @@
+QA output created by 404
+PASS: ciphertexts were not repeated for contents
+2
diff --git a/tests/generic/group b/tests/generic/group
index a0d6e84..d310654 100644
--- a/tests/generic/group
+++ b/tests/generic/group
@@ -400,3 +400,4 @@
 401 auto quick encrypt
 402 auto quick encrypt
 403 auto quick encrypt
+404 auto encrypt
-- 
2.8.0.rc3.226.g39d4020


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 1/6] generic: add utilities for testing filesystem encryption
  2016-12-05 19:21 ` [PATCH v3 1/6] generic: add utilities for testing filesystem encryption Eric Biggers
@ 2016-12-13  5:41   ` Eryu Guan
  2016-12-13  7:05     ` Darrick J. Wong
  0 siblings, 1 reply; 13+ messages in thread
From: Eryu Guan @ 2016-12-13  5:41 UTC (permalink / raw)
  To: Eric Biggers
  Cc: fstests, Theodore Ts'o, Jaegeuk Kim, Richard Weinberger,
	David Gstir, Michael Halcrow

On Mon, Dec 05, 2016 at 11:21:04AM -0800, Eric Biggers wrote:
> Add utility functions for testing filesystem-level encryption via the
> common API currently supported by ext4 and f2fs, in development for
> ubifs and planned for xfs.  Setting and getting encryption policies will
> use new commands being added to xfs_io, while adding and removing
> encryption keys will use keyctl.
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>
> ---
>  common/config  |   1 +
>  common/encrypt | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 138 insertions(+)
>  create mode 100644 common/encrypt
> 
> diff --git a/common/config b/common/config
> index f0f08d2..3727ec0 100644
> --- a/common/config
> +++ b/common/config
> @@ -202,6 +202,7 @@ export DEBUGFS_PROG="`set_prog_path debugfs`"
>  export UUIDGEN_PROG="`set_prog_path uuidgen`"
>  export GETRICHACL_PROG="`set_prog_path getrichacl`"
>  export SETRICHACL_PROG="`set_prog_path setrichacl`"
> +export KEYCTL_PROG="`set_prog_path keyctl`"
>  
>  # 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/encrypt b/common/encrypt
> new file mode 100644
> index 0000000..e18068e
> --- /dev/null
> +++ b/common/encrypt
> @@ -0,0 +1,137 @@
> +#-----------------------------------------------------------------------
> +#
> +# Common functions for testing filesystem-level encryption
> +#
> +#-----------------------------------------------------------------------
> +# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
> +#
> +# Author: Eric Biggers <ebiggers@google.com>
> +#
> +# This program is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU General Public License as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it would be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write the Free Software Foundation,
> +# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> +#-----------------------------------------------------------------------
> +
> +_require_encryption()

I think it's better to name it as _require_scratch_encryption (like
_require_scratch_reflink), so that we know it takes use of SCRATCH_DEV
and tests against it to check if encryption is supported.

> +{
> +	# The 'test_dummy_encryption' mount option interferes with trying to use
> +	# encryption for real, even if we are just trying to get/set policies
> +	# and never put any keys in the keyring.  So skip the real encryption
> +	# tests if the 'test_dummy_encryption' mount option was specified.
> +	if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
> +		_notrun "Dummy encryption is on; skipping real encryption tests"
> +	fi

There's a helper _exclude_scratch_mount_option to do this.

> +
> +	# Make a filesystem on the scratch device with the encryption feature
> +	# enabled.  If this fails then probably the userspace tools (e.g.
> +	# e2fsprogs or f2fs-tools) are too old to understand encryption.
> +	if ! _scratch_mkfs_encrypted &>>$seqres.full; then
> +		_notrun "$FSTYP userspace tools do not support encryption"
> +	fi
> +
> +	# Try to mount the filesystem.  If this fails then either the kernel
> +	# isn't aware of encryption, or the mkfs options were not compatible
> +	# with encryption (e.g. ext4 with block size != PAGE_SIZE).
> +	if ! _scratch_mount &>>$seqres.full; then
> +		_notrun "kernel is unaware of $FSTYP encryption feature, " \
> +			"or mkfs options are not compatible with encryption"
> +	fi
> +
> +	# The kernel may be aware of encryption without supporting it.  For
> +	# example, for ext4 this is the case with kernels configured with
> +	# CONFIG_EXT4_FS_ENCRYPTION=n.  Detect support for encryption by trying
> +	# to set an encryption policy.  (For ext4 we could instead check for the
> +	# presence of /sys/fs/ext4/features/encryption, but this is broken on
> +	# some older kernels and is ext4-specific anyway.)
> +	mkdir $SCRATCH_MNT/tmpdir
> +	if $XFS_IO_PROG -c set_encpolicy $SCRATCH_MNT/tmpdir \
> +		2>&1 >>$seqres.full | \
> +		egrep -q 'Inappropriate ioctl for device|Operation not supported'
> +	then
> +		_notrun "kernel does not support $FSTYP encryption"
> +	fi
> +	rmdir $SCRATCH_MNT/tmpdir
> +	_scratch_unmount
> +}
> +
> +_scratch_mkfs_encrypted()
> +{
> +	_scratch_mkfs -O encrypt

Do case switch based on FSTYP (like what you do in v1 patch), just don't
override MKFS_OPTIONS as Dave pointed out.

_scratch_mkfs_encrypted()
{
	case $FSTYP in
	ext4|f2fs)
		_scratch_mkfs -O encrypt
		;;
	*)
		_notrun "No encryption support for $FSTYP"
		;;
	esac
}

> +
> +# Give the invoking shell a new session keyring.  This makes any keys we add to
> +# the session keyring scoped to the lifetime of the test script.
> +_new_session_keyring()
> +{
> +	$KEYCTL_PROG new_session >>$seqres.full
> +}
> +
> +#
> +# Generate a random encryption key, add it to the session keyring, and print out
> +# the resulting key descriptor (example: "8bf798e1a494e1ec").  Requires the
> +# keyctl program.  It's assumed the caller has already set up a test-scoped
> +# session keyring using _new_session_keyring.
> +#
> +_generate_encryption_key()
> +{
> +	# Generate a key descriptor (16 character hex string)
> +	local keydesc=""
> +	for ((i = 0; i < 8; i++)); do
> +		keydesc="${keydesc}$(printf "%02x" $(( $RANDOM % 256 )))"
> +	done
> +
> +	# Generate the actual encryption key (64 bytes)
> +	local raw=""
> +	for ((i = 0; i < 64; i++)); do
> +		raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))"
> +	done
> +
> +	#
> +	# Add the key to the session keyring.  The required structure is:
> +	#
> +	#	#define FS_MAX_KEY_SIZE 64
> +	#	struct fscrypt_key {
> +	#		u32 mode;
> +	#		u8 raw[FS_MAX_KEY_SIZE];
> +	#		u32 size;
> +	#	} __packed;
> +	#
> +	# The kernel ignores 'mode' but requires that 'size' be 64.
> +	#
> +	# Keys are named $FSTYP:KEYDESC where KEYDESC is the 16-character key
> +	# descriptor hex string.  Newer kernels (ext4 4.8 and later, f2fs 4.6
> +	# and later) also allow the common key prefix "fscrypt:" in addition to
> +	# their filesystem-specific key prefix ("ext4:", "f2fs:").  It would be
> +	# nice to use the common key prefix, but for now use the filesystem-
> +	# specific prefix to make it possible to test older kernels...
> +	#
> +	local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
> +			   cut -f2 -d' ' | cut -c1 )
> +	if (( big_endian )); then
> +		local mode='\x00\x00\x00\x00'
> +		local size='\x00\x00\x00\x40'
> +	else
> +		local mode='\x00\x00\x00\x00'
> +		local size='\x40\x00\x00\x00'
> +	fi
> +	echo -n -e "${mode}${raw}${size}" |
> +		$KEYCTL_PROG padd logon $FSTYP:$keydesc @s >>$seqres.full
> +	echo $keydesc
> +}
> +
> +# Unlink an encryption key from the session keyring, given its key descriptor.
> +_unlink_encryption_key()
> +{
> +	local keydesc=$1
> +	local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
> +	$KEYCTL_PROG unlink $keyid >>$seqres.full
> +}
> -- 
> 2.8.0.rc3.226.g39d4020
> 
> --
> To unsubscribe from this list: send the line "unsubscribe fstests" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/6] generic: test setting and getting encryption policies
  2016-12-05 19:21 ` [PATCH v3 2/6] generic: test setting and getting encryption policies Eric Biggers
@ 2016-12-13  5:59   ` Eryu Guan
  0 siblings, 0 replies; 13+ messages in thread
From: Eryu Guan @ 2016-12-13  5:59 UTC (permalink / raw)
  To: Eric Biggers
  Cc: fstests, Theodore Ts'o, Jaegeuk Kim, Richard Weinberger,
	David Gstir, Michael Halcrow

On Mon, Dec 05, 2016 at 11:21:05AM -0800, Eric Biggers wrote:
> Several kernel bugs were recently fixed regarding the constraints for
> setting encryption policies.  Add tests for these cases and a few more.
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>
> ---
>  tests/generic/400     | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/400.out |  43 ++++++++++++++++
>  tests/generic/group   |   1 +
>  3 files changed, 176 insertions(+)
>  create mode 100755 tests/generic/400
>  create mode 100644 tests/generic/400.out
> 
> diff --git a/tests/generic/400 b/tests/generic/400
> new file mode 100755
> index 0000000..6bcc65d
> --- /dev/null
> +++ b/tests/generic/400
> @@ -0,0 +1,132 @@
> +#! /bin/bash
> +# FS QA Test generic/400
> +#
> +# Test setting and getting encryption policies.
> +#
> +#-----------------------------------------------------------------------
> +# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
> +#
> +# Author: Eric Biggers <ebiggers@google.com>
> +#
> +# This program is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU General Public License as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it would be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write the Free Software Foundation,
> +# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> +#-----------------------------------------------------------------------
> +#
> +
> +seq=`basename $0`
> +seqres=$RESULT_DIR/$seq
> +echo "QA output created by $seq"
> +
> +here=`pwd`
> +tmp=/tmp/$$
> +status=1	# failure is the default!
> +trap "_cleanup; exit \$status" 0 1 2 3 15
> +
> +_cleanup()
> +{
> +	cd /
> +	rm -f $tmp.*
> +}
> +
> +# get standard environment, filters and checks
> +. ./common/rc
> +. ./common/filter
> +. ./common/encrypt
> +
> +# remove previous $seqres.full before test
> +rm -f $seqres.full
> +
> +# real QA test starts here
> +_supported_fs ext4 f2fs

The supported fs for generic tests should be "generic", we rule out all
unsupported filesystems by all the _require rules. All other tests have
this problem.

> +_supported_os Linux
> +_require_xfs_io_command "get_encpolicy"
> +_require_xfs_io_command "set_encpolicy"
> +_require_scratch
> +_require_encryption
> +_require_user
> +
> +_scratch_mkfs_encrypted >> $seqres.full
> +_scratch_mount
> +cd $SCRATCH_MNT

As Dave said, we usually don't cd to $SCRATCH_MNT, we usually do

empty_dir=$SCRATCH_MNT/empty_dir
mkdir $empty_dir
<other tests on $empty_dir>

And replace the actual $SCRATCH_MNT with "SCRATCH_MNT" by using
_filter_scratch. You do this in patch "4/6" :)

Thanks,
Eryu

> +
> +check_no_policy()
> +{
> +	# When a file is unencrypted, FS_IOC_GET_ENCRYPTION_POLICY currently
> +	# fails with ENOENT on ext4 but with ENODATA on f2fs.  TODO: it's
> +	# planned to consistently use ENODATA.  For now this test accepts both.
> +	$XFS_IO_PROG -c "get_encpolicy" $1 |&
> +		sed -e 's/No such file or directory/No data available/'
> +}
> +
> +# Should be able to set an encryption policy on an empty directory
> +echo -e "\n*** Setting encryption policy on empty directory ***"
> +mkdir empty_dir
> +check_no_policy empty_dir
> +$XFS_IO_PROG -c "set_encpolicy 0000111122223333" empty_dir
> +$XFS_IO_PROG -c "get_encpolicy" empty_dir
> +
> +# Should be able to set the same policy again, but not a different one.
> +# TODO: the error code for "already has a different policy" is planned to switch
> +# from EINVAL to EEXIST.  For now this test accepts both.
> +echo -e "\n*** Setting encryption policy again ***"
> +$XFS_IO_PROG -c "set_encpolicy 0000111122223333" empty_dir
> +$XFS_IO_PROG -c "get_encpolicy" empty_dir
> +$XFS_IO_PROG -c "set_encpolicy 4444555566667777" empty_dir |& \
> +	sed -e 's/Invalid argument/File exists/'
> +$XFS_IO_PROG -c "get_encpolicy" empty_dir
> +
> +# Should *not* be able to set an encryption policy on a nonempty directory
> +echo -e "\n*** Setting encryption policy on nonempty directory ***"
> +mkdir nonempty_dir
> +touch nonempty_dir/file
> +$XFS_IO_PROG -c "set_encpolicy" nonempty_dir
> +check_no_policy nonempty_dir
> +
> +# Should *not* be able to set an encryption policy on a nondirectory file, even
> +# an empty one.  Regression test for 002ced4be642: "fscrypto: only allow setting
> +# encryption policy on directories".
> +# TODO: the error code for "not a directory" is planned to switch from EINVAL to
> +# ENOTDIR.  For now this test accepts both.
> +echo -e "\n*** Setting encryption policy on nondirectory ***"
> +touch nondirectory
> +$XFS_IO_PROG -c "set_encpolicy" nondirectory |& \
> +	sed -e 's/Invalid argument/Not a directory/'
> +check_no_policy nondirectory
> +
> +# Should *not* be able to set an encryption policy on another user's directory.
> +# Regression test for 163ae1c6ad62: "fscrypto: add authorization check for
> +# setting encryption policy".
> +echo -e "\n*** Setting encryption policy on another user's directory ***"
> +mkdir unauthorized_dir
> +su $qa_user -c "$XFS_IO_PROG -c \"set_encpolicy\" unauthorized_dir"
> +check_no_policy unauthorized_dir
> +
> +# Should *not* be able to set an encryption policy on a directory on a
> +# filesystem mounted readonly.  Regression test for ba63f23d69a3: "fscrypto:
> +# require write access to mount to set encryption policy".  Test both a regular
> +# readonly filesystem and a readonly bind mount of a read-write filesystem.
> +echo -e "\n*** Setting encryption policy on readonly filesystem ***"
> +mkdir ro_dir ro_bind_mnt
> +_scratch_remount ro
> +$XFS_IO_PROG -c "set_encpolicy" ro_dir
> +check_no_policy ro_dir
> +_scratch_remount rw
> +mount --bind $SCRATCH_MNT ro_bind_mnt
> +mount -o remount,ro ro_bind_mnt
> +$XFS_IO_PROG -c "set_encpolicy" ro_bind_mnt/ro_dir
> +check_no_policy ro_bind_mnt/ro_dir
> +umount ro_bind_mnt
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/generic/400.out b/tests/generic/400.out
> new file mode 100644
> index 0000000..d187841
> --- /dev/null
> +++ b/tests/generic/400.out
> @@ -0,0 +1,43 @@
> +QA output created by 400
> +
> +*** Setting encryption policy on empty directory ***
> +empty_dir: failed to get encryption policy: No data available
> +Encryption policy for empty_dir:
> +	Policy version: 0
> +	Master key descriptor: 0000111122223333
> +	Contents encryption mode: 1 (AES-256-XTS)
> +	Filenames encryption mode: 4 (AES-256-CTS)
> +	Flags: 0x02
> +
> +*** Setting encryption policy again ***
> +Encryption policy for empty_dir:
> +	Policy version: 0
> +	Master key descriptor: 0000111122223333
> +	Contents encryption mode: 1 (AES-256-XTS)
> +	Filenames encryption mode: 4 (AES-256-CTS)
> +	Flags: 0x02
> +empty_dir: failed to set encryption policy: File exists
> +Encryption policy for empty_dir:
> +	Policy version: 0
> +	Master key descriptor: 0000111122223333
> +	Contents encryption mode: 1 (AES-256-XTS)
> +	Filenames encryption mode: 4 (AES-256-CTS)
> +	Flags: 0x02
> +
> +*** Setting encryption policy on nonempty directory ***
> +nonempty_dir: failed to set encryption policy: Directory not empty
> +nonempty_dir: failed to get encryption policy: No data available
> +
> +*** Setting encryption policy on nondirectory ***
> +nondirectory: failed to set encryption policy: Not a directory
> +nondirectory: failed to get encryption policy: No data available
> +
> +*** Setting encryption policy on another user's directory ***
> +unauthorized_dir: failed to set encryption policy: Permission denied
> +unauthorized_dir: failed to get encryption policy: No data available
> +
> +*** Setting encryption policy on readonly filesystem ***
> +ro_dir: failed to set encryption policy: Read-only file system
> +ro_dir: failed to get encryption policy: No data available
> +ro_bind_mnt/ro_dir: failed to set encryption policy: Read-only file system
> +ro_bind_mnt/ro_dir: failed to get encryption policy: No data available
> diff --git a/tests/generic/group b/tests/generic/group
> index f4af986..e6619ca 100644
> --- a/tests/generic/group
> +++ b/tests/generic/group
> @@ -396,3 +396,4 @@
>  391 auto quick rw
>  392 auto quick metadata
>  393 auto quick rw
> +400 auto quick encrypt
> -- 
> 2.8.0.rc3.226.g39d4020
> 
> --
> To unsubscribe from this list: send the line "unsubscribe fstests" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree
  2016-12-05 19:21 ` [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree Eric Biggers
@ 2016-12-13  6:07   ` Eryu Guan
  0 siblings, 0 replies; 13+ messages in thread
From: Eryu Guan @ 2016-12-13  6:07 UTC (permalink / raw)
  To: Eric Biggers
  Cc: fstests, Theodore Ts'o, Jaegeuk Kim, Richard Weinberger,
	David Gstir, Michael Halcrow

On Mon, Dec 05, 2016 at 11:21:08AM -0800, Eric Biggers wrote:
> Add an xfstest which partially verifies that the filesystem enforces
> that all files in an encrypted directory tree use the same encryption
> policy.
> 
> Signed-off-by: Eric Biggers <ebiggers@google.com>
> ---
>  tests/generic/403     | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  tests/generic/403.out |  34 +++++++++++++
>  tests/generic/group   |   1 +
>  3 files changed, 165 insertions(+)
>  create mode 100644 tests/generic/403
>  create mode 100644 tests/generic/403.out
> 
> diff --git a/tests/generic/403 b/tests/generic/403
> new file mode 100644
> index 0000000..77427b8
> --- /dev/null
> +++ b/tests/generic/403
> @@ -0,0 +1,130 @@
> +#! /bin/bash
> +# FS QA Test generic/403
> +#
> +# Filesystem encryption is designed to enforce that a consistent encryption
> +# policy is used within a given encrypted directory tree and that an encrypted
> +# directory tree does not contain any unencrypted files.  This test verifies
> +# that filesystem operations that would violate this constraint fail with EPERM.
> +# This does not yet test enforcement of this constraint on lookup, which is
> +# needed to detect offline changes.
> +#
> +#-----------------------------------------------------------------------
> +# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
> +#
> +# Author: Eric Biggers <ebiggers@google.com>
> +#
> +# This program is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU General Public License as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it would be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write the Free Software Foundation,
> +# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> +#-----------------------------------------------------------------------
> +#
> +
> +seq=`basename $0`
> +seqres=$RESULT_DIR/$seq
> +echo "QA output created by $seq"
> +
> +here=`pwd`
> +tmp=/tmp/$$
> +status=1	# failure is the default!
> +trap "_cleanup; exit \$status" 0 1 2 3 15
> +
> +_cleanup()
> +{
> +	cd /
> +	rm -f $tmp.*
> +}
> +
> +# get standard environment, filters and checks
> +. ./common/rc
> +. ./common/filter
> +. ./common/encrypt
> +. ./common/renameat2
> +
> +# remove previous $seqres.full before test
> +rm -f $seqres.full
> +
> +# real QA test starts here
> +_supported_fs ext4 f2fs
> +_supported_os Linux
> +_require_xfs_io_command "set_encpolicy"
> +_require_scratch
> +_require_encryption
> +_requires_renameat2
> +
> +_new_session_keyring
> +_scratch_mkfs_encrypted >> $seqres.full
> +_scratch_mount
> +
> +# Set up two encrypted directories, with different encryption policies,
> +# and one unencrypted directory.
> +edir1=$SCRATCH_MNT/edir1
> +edir2=$SCRATCH_MNT/edir2
> +udir=$SCRATCH_MNT/udir
> +mkdir $edir1 $edir2 $udir
> +keydesc1=$(_generate_encryption_key)
> +keydesc2=$(_generate_encryption_key)
> +$XFS_IO_PROG -c "set_encpolicy $keydesc1" $edir1
> +$XFS_IO_PROG -c "set_encpolicy $keydesc2" $edir2
> +touch $edir1/efile1
> +touch $edir2/efile2
> +touch $udir/ufile
> +
> +echo -e "\n*** Link encrypted <= encrypted ***"
> +ln $edir1/efile1 $edir2/efile1 |& _filter_scratch
> +
> +echo -e "\n*** Rename encrypted => encrypted ***"
> +mv $edir1/efile1 $edir2/efile1 |& _filter_scratch
> +
> +echo -e "\n*** Exchange encrypted <=> encrypted ***"
> +src/renameat2 -x $edir1/efile1 $edir2/efile2 |& _filter_scratch
> +
> +
> +echo -e "\n\n*** Link unencrypted <= encrypted ***"
> +ln $udir/ufile $edir1/ufile |& _filter_scratch
> +
> +echo -e "\n*** Rename unencrypted => encrypted ***"
> +mv $udir/ufile $edir1/ufile |& _filter_scratch
> +
> +echo -e "\n*** Exchange unencrypted <=> encrypted ***"
> +src/renameat2 -x $udir/ufile $edir1/efile1 |& _filter_scratch
> +
> +
> +echo -e "\n\n*** Link encrypted <= unencrypted ***"
> +ln -v $edir1/efile1 $udir/efile1 |& _filter_scratch # should succeed
> +rm $udir/efile1 # undo
> +
> +echo -e "\n*** Rename encrypted => unencrypted ***"
> +mv -v $edir1/efile1 $udir/efile1 |& _filter_scratch # should succeed
> +mv $udir/efile1 $edir1/efile1 # undo
> +
> +echo -e "\n*** Exchange encrypted <=> unencrypted ***"
> +src/renameat2 -x $edir1/efile1 $udir/ufile |& _filter_scratch
> +
> +# Now test the cases where we don't have access to the encryption keys.
> +
> +_unlink_encryption_key $keydesc1
> +_unlink_encryption_key $keydesc2
> +_scratch_cycle_mount
> +efile1=$(find $edir1 -type f)
> +efile2=$(find $edir2 -type f)
> +echo
> +
> +# TODO: this currently succeeds.  It should fail.  Fix this kernel-side.
> +#echo -e "\n*** Exchange encrypted <=> encrypted without key ***"
> +#src/renameat2 -x $efile1 $efile2

If it reveals a kernel bug, just uncomment it & update the .out file and
let it fail, as long as it's not a kernel crash, so it acts as a
reminder that there's an unfixed bug :)

For kernel-crashing tests, we tend to merge them after there's a known
fix, at least in maintainer's tree. But if it's a bug that won't be
fixed in the near future, we can merge such tests with "dangerous"
group, so they can be skipped by using "-x dangerous" option to check.

Thanks,
Eryu

> +
> +echo -e "\n*** Exchange encrypted <=> unencrypted without key ***"
> +src/renameat2 -x $efile1 $udir/ufile
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/generic/403.out b/tests/generic/403.out
> new file mode 100644
> index 0000000..27ed8cb
> --- /dev/null
> +++ b/tests/generic/403.out
> @@ -0,0 +1,34 @@
> +QA output created by 403
> +
> +*** Link encrypted <= encrypted ***
> +ln: failed to create hard link 'SCRATCH_MNT/edir2/efile1' => 'SCRATCH_MNT/edir1/efile1': Operation not permitted
> +
> +*** Rename encrypted => encrypted ***
> +mv: cannot move 'SCRATCH_MNT/edir1/efile1' to 'SCRATCH_MNT/edir2/efile1': Operation not permitted
> +
> +*** Exchange encrypted <=> encrypted ***
> +Operation not permitted
> +
> +
> +*** Link unencrypted <= encrypted ***
> +ln: failed to create hard link 'SCRATCH_MNT/edir1/ufile' => 'SCRATCH_MNT/udir/ufile': Operation not permitted
> +
> +*** Rename unencrypted => encrypted ***
> +mv: cannot move 'SCRATCH_MNT/udir/ufile' to 'SCRATCH_MNT/edir1/ufile': Operation not permitted
> +
> +*** Exchange unencrypted <=> encrypted ***
> +Operation not permitted
> +
> +
> +*** Link encrypted <= unencrypted ***
> +'SCRATCH_MNT/udir/efile1' => 'SCRATCH_MNT/edir1/efile1'
> +
> +*** Rename encrypted => unencrypted ***
> +'SCRATCH_MNT/edir1/efile1' -> 'SCRATCH_MNT/udir/efile1'
> +
> +*** Exchange encrypted <=> unencrypted ***
> +Operation not permitted
> +
> +
> +*** Exchange encrypted <=> unencrypted without key ***
> +Operation not permitted
> diff --git a/tests/generic/group b/tests/generic/group
> index e218380..a0d6e84 100644
> --- a/tests/generic/group
> +++ b/tests/generic/group
> @@ -399,3 +399,4 @@
>  400 auto quick encrypt
>  401 auto quick encrypt
>  402 auto quick encrypt
> +403 auto quick encrypt
> -- 
> 2.8.0.rc3.226.g39d4020
> 
> --
> To unsubscribe from this list: send the line "unsubscribe fstests" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/6] Add filesystem-level encryption tests
  2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
                   ` (5 preceding siblings ...)
  2016-12-05 19:21 ` [PATCH v3 6/6] generic: test for weaknesses in filesystem encryption Eric Biggers
@ 2016-12-13  6:12 ` Eryu Guan
  2016-12-15  0:31   ` Eric Biggers
  6 siblings, 1 reply; 13+ messages in thread
From: Eryu Guan @ 2016-12-13  6:12 UTC (permalink / raw)
  To: Eric Biggers
  Cc: fstests, Theodore Ts'o, Jaegeuk Kim, Richard Weinberger,
	David Gstir, Michael Halcrow

On Mon, Dec 05, 2016 at 11:21:03AM -0800, Eric Biggers wrote:
> This is the third version of the patch series to add some xfstests for
> filesystem-level encryption.  The new tests are designed to run on any
> filesystem that implements the "fscrypt" API, currently ext4 and f2fs.
> (fscrypt support for ubifs is also under development, but it looks like
> xfstests doesn't have ubifs support yet.)
> 
> The new tests are designed to complement, not replace, doing a full
> xfstests run with encryption enabled, which for ext4 can currently be
> done by using the test_dummy_encryption mount option.

These tests look good to me now overall, from fstests perspective of
view. I tested them with 4.9 kernel with ext4 encryption enabled, latest
e2fsprogs and latest xfsprogs plus the set|get_policy patch. All tests
passed on ext4. Comments go to individual patch.

But I'm not so familiar with this new encryption API, it would be good
to have some reviews on the tests from other fscrypt developers too.

Thanks,
Eryu

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 1/6] generic: add utilities for testing filesystem encryption
  2016-12-13  5:41   ` Eryu Guan
@ 2016-12-13  7:05     ` Darrick J. Wong
  0 siblings, 0 replies; 13+ messages in thread
From: Darrick J. Wong @ 2016-12-13  7:05 UTC (permalink / raw)
  To: Eryu Guan
  Cc: Eric Biggers, fstests, Theodore Ts'o, Jaegeuk Kim,
	Richard Weinberger, David Gstir, Michael Halcrow

On Tue, Dec 13, 2016 at 01:41:55PM +0800, Eryu Guan wrote:
> On Mon, Dec 05, 2016 at 11:21:04AM -0800, Eric Biggers wrote:
> > Add utility functions for testing filesystem-level encryption via the
> > common API currently supported by ext4 and f2fs, in development for
> > ubifs and planned for xfs.  Setting and getting encryption policies will
> > use new commands being added to xfs_io, while adding and removing
> > encryption keys will use keyctl.
> > 
> > Signed-off-by: Eric Biggers <ebiggers@google.com>
> > ---
> >  common/config  |   1 +
> >  common/encrypt | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 138 insertions(+)
> >  create mode 100644 common/encrypt
> > 
> > diff --git a/common/config b/common/config
> > index f0f08d2..3727ec0 100644
> > --- a/common/config
> > +++ b/common/config
> > @@ -202,6 +202,7 @@ export DEBUGFS_PROG="`set_prog_path debugfs`"
> >  export UUIDGEN_PROG="`set_prog_path uuidgen`"
> >  export GETRICHACL_PROG="`set_prog_path getrichacl`"
> >  export SETRICHACL_PROG="`set_prog_path setrichacl`"
> > +export KEYCTL_PROG="`set_prog_path keyctl`"
> >  
> >  # 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/encrypt b/common/encrypt
> > new file mode 100644
> > index 0000000..e18068e
> > --- /dev/null
> > +++ b/common/encrypt
> > @@ -0,0 +1,137 @@
> > +#-----------------------------------------------------------------------
> > +#
> > +# Common functions for testing filesystem-level encryption
> > +#
> > +#-----------------------------------------------------------------------
> > +# Copyright (c) 2016 Google, Inc.  All Rights Reserved.
> > +#
> > +# Author: Eric Biggers <ebiggers@google.com>
> > +#
> > +# This program is free software; you can redistribute it and/or
> > +# modify it under the terms of the GNU General Public License as
> > +# published by the Free Software Foundation.
> > +#
> > +# This program is distributed in the hope that it would be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +# GNU General Public License for more details.
> > +#
> > +# You should have received a copy of the GNU General Public License
> > +# along with this program; if not, write the Free Software Foundation,
> > +# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> > +#-----------------------------------------------------------------------
> > +
> > +_require_encryption()
> 
> I think it's better to name it as _require_scratch_encryption (like
> _require_scratch_reflink), so that we know it takes use of SCRATCH_DEV
> and tests against it to check if encryption is supported.

Agree, since the function is only capable of testing the scratch device.

--D

> > +{
> > +	# The 'test_dummy_encryption' mount option interferes with trying to use
> > +	# encryption for real, even if we are just trying to get/set policies
> > +	# and never put any keys in the keyring.  So skip the real encryption
> > +	# tests if the 'test_dummy_encryption' mount option was specified.
> > +	if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
> > +		_notrun "Dummy encryption is on; skipping real encryption tests"
> > +	fi
> 
> There's a helper _exclude_scratch_mount_option to do this.
> 
> > +
> > +	# Make a filesystem on the scratch device with the encryption feature
> > +	# enabled.  If this fails then probably the userspace tools (e.g.
> > +	# e2fsprogs or f2fs-tools) are too old to understand encryption.
> > +	if ! _scratch_mkfs_encrypted &>>$seqres.full; then
> > +		_notrun "$FSTYP userspace tools do not support encryption"
> > +	fi
> > +
> > +	# Try to mount the filesystem.  If this fails then either the kernel
> > +	# isn't aware of encryption, or the mkfs options were not compatible
> > +	# with encryption (e.g. ext4 with block size != PAGE_SIZE).
> > +	if ! _scratch_mount &>>$seqres.full; then
> > +		_notrun "kernel is unaware of $FSTYP encryption feature, " \
> > +			"or mkfs options are not compatible with encryption"
> > +	fi
> > +
> > +	# The kernel may be aware of encryption without supporting it.  For
> > +	# example, for ext4 this is the case with kernels configured with
> > +	# CONFIG_EXT4_FS_ENCRYPTION=n.  Detect support for encryption by trying
> > +	# to set an encryption policy.  (For ext4 we could instead check for the
> > +	# presence of /sys/fs/ext4/features/encryption, but this is broken on
> > +	# some older kernels and is ext4-specific anyway.)
> > +	mkdir $SCRATCH_MNT/tmpdir
> > +	if $XFS_IO_PROG -c set_encpolicy $SCRATCH_MNT/tmpdir \
> > +		2>&1 >>$seqres.full | \
> > +		egrep -q 'Inappropriate ioctl for device|Operation not supported'
> > +	then
> > +		_notrun "kernel does not support $FSTYP encryption"
> > +	fi
> > +	rmdir $SCRATCH_MNT/tmpdir
> > +	_scratch_unmount
> > +}
> > +
> > +_scratch_mkfs_encrypted()
> > +{
> > +	_scratch_mkfs -O encrypt
> 
> Do case switch based on FSTYP (like what you do in v1 patch), just don't
> override MKFS_OPTIONS as Dave pointed out.
> 
> _scratch_mkfs_encrypted()
> {
> 	case $FSTYP in
> 	ext4|f2fs)
> 		_scratch_mkfs -O encrypt
> 		;;
> 	*)
> 		_notrun "No encryption support for $FSTYP"
> 		;;
> 	esac
> }
> 
> > +
> > +# Give the invoking shell a new session keyring.  This makes any keys we add to
> > +# the session keyring scoped to the lifetime of the test script.
> > +_new_session_keyring()
> > +{
> > +	$KEYCTL_PROG new_session >>$seqres.full
> > +}
> > +
> > +#
> > +# Generate a random encryption key, add it to the session keyring, and print out
> > +# the resulting key descriptor (example: "8bf798e1a494e1ec").  Requires the
> > +# keyctl program.  It's assumed the caller has already set up a test-scoped
> > +# session keyring using _new_session_keyring.
> > +#
> > +_generate_encryption_key()
> > +{
> > +	# Generate a key descriptor (16 character hex string)
> > +	local keydesc=""
> > +	for ((i = 0; i < 8; i++)); do
> > +		keydesc="${keydesc}$(printf "%02x" $(( $RANDOM % 256 )))"
> > +	done
> > +
> > +	# Generate the actual encryption key (64 bytes)
> > +	local raw=""
> > +	for ((i = 0; i < 64; i++)); do
> > +		raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))"
> > +	done
> > +
> > +	#
> > +	# Add the key to the session keyring.  The required structure is:
> > +	#
> > +	#	#define FS_MAX_KEY_SIZE 64
> > +	#	struct fscrypt_key {
> > +	#		u32 mode;
> > +	#		u8 raw[FS_MAX_KEY_SIZE];
> > +	#		u32 size;
> > +	#	} __packed;
> > +	#
> > +	# The kernel ignores 'mode' but requires that 'size' be 64.
> > +	#
> > +	# Keys are named $FSTYP:KEYDESC where KEYDESC is the 16-character key
> > +	# descriptor hex string.  Newer kernels (ext4 4.8 and later, f2fs 4.6
> > +	# and later) also allow the common key prefix "fscrypt:" in addition to
> > +	# their filesystem-specific key prefix ("ext4:", "f2fs:").  It would be
> > +	# nice to use the common key prefix, but for now use the filesystem-
> > +	# specific prefix to make it possible to test older kernels...
> > +	#
> > +	local big_endian=$(echo -ne '\x11' | od -tx2 | head -1 | \
> > +			   cut -f2 -d' ' | cut -c1 )
> > +	if (( big_endian )); then
> > +		local mode='\x00\x00\x00\x00'
> > +		local size='\x00\x00\x00\x40'
> > +	else
> > +		local mode='\x00\x00\x00\x00'
> > +		local size='\x40\x00\x00\x00'
> > +	fi
> > +	echo -n -e "${mode}${raw}${size}" |
> > +		$KEYCTL_PROG padd logon $FSTYP:$keydesc @s >>$seqres.full
> > +	echo $keydesc
> > +}
> > +
> > +# Unlink an encryption key from the session keyring, given its key descriptor.
> > +_unlink_encryption_key()
> > +{
> > +	local keydesc=$1
> > +	local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
> > +	$KEYCTL_PROG unlink $keyid >>$seqres.full
> > +}
> > -- 
> > 2.8.0.rc3.226.g39d4020
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe fstests" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe fstests" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/6] Add filesystem-level encryption tests
  2016-12-13  6:12 ` [PATCH v3 0/6] Add filesystem-level encryption tests Eryu Guan
@ 2016-12-15  0:31   ` Eric Biggers
  0 siblings, 0 replies; 13+ messages in thread
From: Eric Biggers @ 2016-12-15  0:31 UTC (permalink / raw)
  To: Eryu Guan
  Cc: Eric Biggers, fstests, Theodore Ts'o, Jaegeuk Kim,
	Richard Weinberger, David Gstir, Michael Halcrow

On Tue, Dec 13, 2016 at 02:12:55PM +0800, Eryu Guan wrote:
> On Mon, Dec 05, 2016 at 11:21:03AM -0800, Eric Biggers wrote:
> > This is the third version of the patch series to add some xfstests for
> > filesystem-level encryption.  The new tests are designed to run on any
> > filesystem that implements the "fscrypt" API, currently ext4 and f2fs.
> > (fscrypt support for ubifs is also under development, but it looks like
> > xfstests doesn't have ubifs support yet.)
> > 
> > The new tests are designed to complement, not replace, doing a full
> > xfstests run with encryption enabled, which for ext4 can currently be
> > done by using the test_dummy_encryption mount option.
> 
> These tests look good to me now overall, from fstests perspective of
> view. I tested them with 4.9 kernel with ext4 encryption enabled, latest
> e2fsprogs and latest xfsprogs plus the set|get_policy patch. All tests
> passed on ext4. Comments go to individual patch.
> 
> But I'm not so familiar with this new encryption API, it would be good
> to have some reviews on the tests from other fscrypt developers too.
> 

Hi Eryu, thanks for the review.  I'm planning to send a v4 of the patchset to
address your comments.

I've let people with more involvement in filesystem encryption know that this
needs to be reviewed.  (And some are Cc'ed here; Ted and Jaegeuk are the
official fscrypto maintainers.)

Thanks,

Eric

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2016-12-15  0:31 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-05 19:21 [PATCH v3 0/6] Add filesystem-level encryption tests Eric Biggers
2016-12-05 19:21 ` [PATCH v3 1/6] generic: add utilities for testing filesystem encryption Eric Biggers
2016-12-13  5:41   ` Eryu Guan
2016-12-13  7:05     ` Darrick J. Wong
2016-12-05 19:21 ` [PATCH v3 2/6] generic: test setting and getting encryption policies Eric Biggers
2016-12-13  5:59   ` Eryu Guan
2016-12-05 19:21 ` [PATCH v3 3/6] generic: test validation of encryption policy structure Eric Biggers
2016-12-05 19:21 ` [PATCH v3 4/6] generic: test encrypted file access Eric Biggers
2016-12-05 19:21 ` [PATCH v3 5/6] generic: test enforcement of one encryption policy per tree Eric Biggers
2016-12-13  6:07   ` Eryu Guan
2016-12-05 19:21 ` [PATCH v3 6/6] generic: test for weaknesses in filesystem encryption Eric Biggers
2016-12-13  6:12 ` [PATCH v3 0/6] Add filesystem-level encryption tests Eryu Guan
2016-12-15  0:31   ` Eric Biggers

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.