1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
#!/bin/bash
#
# common.sh - helper functions for fscrypt command-line interface tests
#
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# Use extra-strict mode.
set -e -u -o pipefail
# Don't allow running the test scripts directly. They need to be run via
# run.sh, to set up everything correctly.
if [ -z "${MNT:-}" ] || [ -z "${MNT_ROOT:-}" ]; then
echo 1>&2 "ERROR: This script can only be run via run.sh, not on its own."
exit 1
fi
# Prints an error message, then fails the test by exiting with failure status.
_fail()
{
echo 1>&2 "ERROR: $1"
exit 1
}
# Runs a shell command and expects that it fails.
_expect_failure()
{
if eval "$1"; then
_fail "command unexpectedly succeeded: \"$1\""
fi
}
# Prints a message to mark the beginning of the next part of the test.
_print_header()
{
echo
echo "# $1"
}
# Deletes all files on the test filesystems, including all policies and
# protectors. Leaves the fscrypt metadata directories themselves.
_reset_filesystems()
{
local mnt
for mnt in "$MNT" "$MNT_ROOT"; do
rm -rf "${mnt:?}"/* "${mnt:?}"/.fscrypt/{policies,protectors}/*
done
}
# Prints the number of filesystems that have encryption support enabled.
_get_enabled_fs_count()
{
local count
count=$(fscrypt status | awk '/filesystems supporting encryption/ { print $4 }')
if [ -z "$count" ]; then
_fail "encryption support status line not found"
fi
echo "$count"
}
# Gets the descriptor of the given protector.
_get_protector_descriptor()
{
local mnt=$1
local source=$2
case $source in
custom)
local name=$3
local description="custom protector \\\"$name\\\""
;;
login)
local user=$3
local description="login protector for $user"
;;
*)
_fail "Unknown protector source $source"
esac
local descriptor
descriptor=$(fscrypt status "$mnt" |
awk -F ' *' '{ if ($3 == "'"$description"'") print $1 }')
if [ -z "$descriptor" ]; then
_fail "Can't find $description on $mnt"
fi
echo "$descriptor"
}
# Gets the descriptor of the login protector for $TEST_USER.
_get_login_descriptor()
{
_get_protector_descriptor "$MNT_ROOT" login "$TEST_USER"
}
# Prints the number of filesystems that have fscrypt metadata.
_get_setup_fs_count()
{
local count
count=$(fscrypt status | awk '/filesystems with fscrypt metadata/ { print $5 }')
if [ -z "$count" ]; then
_fail "fscrypt metadata status line not found"
fi
echo "$count"
}
# Removes all fscrypt metadata from the given filesystem.
_rm_metadata()
{
rm -r "${1:?}/.fscrypt"
}
# Runs a shell command, ignoring its output (stdout and stderr) if it succeeds.
# If the command fails, prints its output and fails the test.
_run_noisy_command()
{
if ! eval "$1" &> "$TMPDIR/out"; then
_fail "Command failed: '$1'. Output was: $(cat "$TMPDIR/out")"
fi
}
# Runs the given shell command as the test user.
_user_do()
{
su "$TEST_USER" --shell=/bin/bash --command="export PATH='$PATH'; $1"
}
# Runs the given shell command as the test user and expects it to fail.
_user_do_and_expect_failure()
{
_expect_failure "_user_do '$1'"
}
# Clear the test user's user keyring and unlink it from root's user keyring, if
# it is linked into it.
_cleanup_user_keyrings()
{
local ringid
ringid=$(_user_do "keyctl show @u" | awk '/keyring: _uid/{print $1}')
_user_do "keyctl clear $ringid"
keyctl unlink "$ringid" @u &> /dev/null || true
}
# Gives the test a new session keyring which contains the test user's keyring
# but not root's keyring. Also clears the test user's keyring. This must be
# called at the beginning of the test script as it may re-execute the script.
_setup_session_keyring()
{
# This *should* just use 'keyctl new_session', but that doesn't work if
# the session keyring is owned by a user other than root. So instead we
# have to use 'keyctl session' and re-execute the script.
if [ -z "${FSCRYPT_SESSION_KEYRING_SET:-}" ]; then
export FSCRYPT_SESSION_KEYRING_SET=1
set +e
keyctl session - "$0" |& grep -v '^Joined session keyring'
exit "${PIPESTATUS[0]}"
fi
# Link the test user's keyring into the new session keyring.
keyctl setperm @s 0x3f000000 # all possessor permissions
_user_do "keyctl link @u @s"
# Clear the test user's keyring.
_user_do "keyctl clear @u"
}
# Wraps the 'expect' command to force subprocesses to have 80-column output.
expect()
{
command expect -c 'set stty_init "cols 80"' -f -
}
|