#!/bin/bash
# Copyright (c) 2014, Kevin Walsh. All rights reserved.
#
# 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.
# Multi-purpose script for setting up and running tao and cloudproxy tests.
#
# This script works with the Go implementation of Tao. It assumes all binaries
# have been installed in ${GOPATH}/bin (e.g. via `go install`)
#
# If you wish to use the TPM, you must have taken ownership of the TPM. For
# example, see github.com/google/go-tpm/examples/tpm-takeownership.
set -e # quit script on first error
# INSTALL BEGIN
# Note: This section of code is removed by install.sh
script_path="install.sh"
test_dir=""
test_tpm="no"
verbose="yes"
test_guard="AllowAll"
for arg in "$@"; do
case "$arg" in
-notpm)
test_tpm="no"
shift
;;
-tpm)
test_tpm="yes"
shift
;;
-acls)
test_guard="ACLs"
shift
;;
-datalog)
test_guard="Datalog"
shift
;;
-q)
verbose="no"
shift
;;
-*)
echo "Huh? $arg"
exit 1
;;
esac
done
if [ $# -eq 1 ]; then
test_dir="$1"
fi
if [ ! "$test_dir" ]; then
echo "Usage: $0 [options]
"
echo " Installs tao testing scripts into , which will be created"
echo " if it does not yet exist. If neither -acls nor -datalog is"
echo " specified, then the authorization policy is AllowAll."
echo "Options:"
echo " -notpm Use a fake TPM (the default)."
echo " -tpm Use the TPM."
echo " -acls Use ACL-based guards for Tao domain policy."
echo " -datalog Use Datalog-based guards for Tao domain policy."
echo " -q Be more quiet."
exit 1
fi
if [ -e "$test_dir" -a ! -d "$test_dir" ]; then
echo "$test_dir: path exists but is not a directory"
exit 1
fi
mkdir -p "$test_dir"
# canonicalize
root_dir=$(readlink -e "$(dirname $0)")
test_dir=$(readlink -e "$test_dir")
if [ "$verbose" == "yes" ]; then
echo "Installing tao test scripts into: $test_dir"
fi
# sanity checks
if [ ! -d "$GOPATH/bin" ]; then
echo "install failed: could not find GOPATH bin directory"
exit 1
fi
if [ ! -f "$root_dir/$script_path" -o ! -d "$test_dir" ]; then
echo "install failed: could not canonicalize paths"
exit 1
fi
mkdir -p "$test_dir/scripts"
rm -f "$test_dir/scripts/tao.sh"
sed '/^# INSTALL BEGIN/,/^# INSTALL END/ s/^/## /' "$root_dir/$script_path" \
>"$test_dir/scripts/tao.sh"
chmod +x "$test_dir/scripts/tao.sh"
cd "$test_dir/scripts/"
perl -p -i -e "s|^export TAO_TEST=undef .*\$|export TAO_TEST="\""$test_dir"\""|" tao.sh
for script in "setup.sh" "start.sh" "restart.sh" "monitor.sh" \
"test.sh" "refresh.sh" "stop.sh" "clean.sh" "hash.sh" "help.sh" \
"host.sh" "base64w-encode.sh" "base64w-decode.sh"; do
rm -f $script
ln -s tao.sh $script
done
cd "$test_dir"
rm -f bin
ln -s ${GOPATH}/bin bin
mkdir -p logs
if [ "$test_tpm" == "yes" ]; then
test_root=false
test_stacked=true
else
test_root=true
test_stacked=false
fi
cat < "$test_dir/tao.env"
# Tao/CloudProxy environment variables"
export TAO_TEST="$test_dir" # Also hardcoded into $test_dir/scripts/*.sh
export TAO_ROOTDIR="$root_dir"
export TAO_USE_TPM="$test_tpm"
# Flags for tao programs
export TAO_config_path="${test_dir}/tao.config"
export TAO_guard="$test_guard"
# Flags for tao_admin
export TAO_ADMIN_pass="BogusPass"
# Flags for linux_host
export TAO_HOST_pass="BogusPass"
export TAO_HOST_root="$test_root"
export TAO_HOST_stacked="$test_stacked"
export TAO_HOST_path="${test_dir}/linux_tao_host"
# Flags for glog
export GLOG_v=2
export GLOG_logtostderr="no"
export GLOG_alsologtostderr="no"
export GLOG_stderrthreshold=3 # Only log FATAL to stderr.
export GLOG_log_dir="\${TAO_TEST}/logs"
# Misc.
export TAO_HOSTED_PROGRAMS="
\${TAO_TEST}/bin/demo
\${TAO_TEST}/bin/demo_server
\${TAO_TEST}/bin/client
\${TAO_TEST}/bin/server
\${TAO_TEST}/bin/fclient
\${TAO_TEST}/bin/fserver
\${TAO_TEST}/bin/http_echo_server
\${TAO_TEST}/bin/https_echo_server
"
# BEGIN SETUP VARIABLES
# These variables come from $test_dir/scripts/setup.sh
export GOOGLE_HOST_TAO=""
# END SETUP VARIABLES
END
if [ "$verbose" == "yes" ]; then
cat < long_binary_fil
function shortname()
{
name="$1"
echo "\<${name:0:15}\>"
}
function showenv()
{
cat ${tao_env}
}
function cleanup()
{
rm -f ${TAO_TEST}/logs/*
rm -rf ${TAO_TEST}/{*keys,linux_tao_host,acls,rules,tao.config}
sed -i '/^# BEGIN SETUP VARIABLES/,/^# END SETUP VARIABLES/d' ${tao_env}
echo "# BEGIN SETUP VARIABLES" >> ${tao_env}
echo "# These variables come from ${TAO_TEST}/scripts/setup.sh" >> ${tao_env}
echo 'export GOOGLE_HOST_TAO=""' >> ${tao_env}
echo "# END SETUP VARIABLES" >> ${tao_env}
echo "Cleared all Tao configuration data"
}
function stoptests()
{
echo "Attempting graceful shutdown..."
(if linux_host --shutdown; then sleep 1; fi ) 2>/dev/null | grep -v "^Aborted$" || true
echo "Checking for remaining Tao services and processes..."
# Try to shutdown
killed=0
for prog in $all_tao_progs; do
if pgrep -lx `shortname "$prog"`; then
pkill -x `shortname "$prog"`
killed=1
fi
done
if [ $killed -eq 1 ]; then
sleep 1
echo "Attempted to kill remaining Tao services and processes"
else
echo "No Tao services or processes remaining"
fi
rm -f ${TAO_TEST}/linux_tao_host/admin_socket ${TAO_TEST}/*/*_socket
}
function setup()
{
mkdir -p ${TAO_TEST}/logs
echo "Creating TaoDomain keys and settings."
tao_admin -create -name testing
# This sets:
# $GOOGLE_HOST_TAO # name of underlying host tao, i.e. the TPM (if any)
# GOOGLE_TAO_TPM, GOOGLE_TAO_PCRS, # more details about TPM (if any)
# and GOOGLE_TAO_LINUX # name of the LinuxHost
sed -i '/^# BEGIN SETUP VARIABLES/,/^# END SETUP VARIABLES/d' ${tao_env}
echo "# BEGIN SETUP VARIABLES" >> ${tao_env}
echo "# These variables come from ${TAO_TEST}/scripts/setup.sh" >> ${tao_env}
if [ "$TAO_USE_TPM" == "yes" ]; then
# Don't create a new AIK if one is already present.
echo "Checking ${TAO_TEST}/tpm/aikblob"
pcr17=`tao_admin -getpcr 17`
pcr18=`tao_admin -getpcr 18`
if [ ! -f ${TAO_TEST}/tpm/aikblob ]; then
echo "Creating TPMTao AIK and settings."
rm -rf ${TAO_TEST}/tpm
# The genaik program comes from
# github.com/google/go-tpm/examples/genaik and must be in
# $GOPATH/bin.
mkdir -p ${TAO_TEST}/tpm
genaik --blob ${TAO_TEST}/tpm/aikblob
else
echo "Reusing existing TPMTao AIK."
fi
export GOOGLE_HOST_TAO='tao::TPMTao("dir:tpm")'
export GOOGLE_TAO_PCRS='PCRs("17,18", "'${pcr17}','${pcr18}'")'
tprin=`tao_admin -aikblob ${TAO_TEST}/tpm/aikblob`
export GOOGLE_TAO_TPM=$tprin
# TODO(tmroeder): do this correctly in the Go version once we support
# AIK creation.
echo "export GOOGLE_HOST_TAO='tao::TPMTao(\"dir:tpm\")'" >> ${tao_env}
echo "export GOOGLE_TAO_PCRS='PCRs(\"17,18\", \"${pcr17},${pcr18}\")'" >> ${tao_env}
echo "export GOOGLE_TAO_TPM='$tprin'" >> ${tao_env}
fi
echo "Creating LinuxHost keys and settings."
rm -rf ${TAOHOST_path}
linux_host --create --show=false
linux_host --show >> ${tao_env}
echo "# END SETUP VARIABLES" >> ${tao_env}
echo "Refreshing"
refresh
}
function refresh()
{
source ${tao_env}
# Set up default execution policy.
tao_admin -clear
if [ "${TAO_guard}" == "Datalog" ]; then
# Rule for TPM and PCRs combinations that make for a good OS
tao_admin -add "(forall S: forall TPM: forall PCRs: TrustedPlatform(TPM) and TrustedKernelPCRs(PCRs) and Subprin(S, TPM, PCRs) implies TrustedOS(S))"
# Rule for OS and program hash that make for a good hosted program
tao_admin -add "(forall P: forall OS: forall Hash: TrustedOS(OS) and TrustedProgramHash(Hash) and Subprin(P, OS, Hash) implies MemberProgram(P))"
# Rule for programs that can execute
tao_admin -add "(forall P: MemberProgram(P) implies Authorized(P, \"Execute\"))"
# Rule for programs with Args subprincipals
tao_admin -add "(forall Y: forall P: forall S: MemberProgram(P) and TrustedArgs(S) and Subprin(Y, P, S) implies Authorized(Y, \"Execute\"))"
# Add the TPM keys, PCRs, and/or LinuxHost keys
if [ "$TAO_USE_TPM" == "yes" ]; then
tao_admin -add 'TrustedPlatform('${GOOGLE_TAO_TPM}')'
# Escape the spaces and quotes in the string so it can be passed as
# a single argument to tao_admin
trustedpcrs=`echo 'TrustedKernelPCRs(ext.'${GOOGLE_TAO_PCRS}')' | sed 's/ /\\ /g' | sed 's/"/\\"/g'`
tao_admin -add "$trustedpcrs"
else
tao_admin -add 'TrustedOS('${GOOGLE_TAO_LINUX}')'
fi
# Add the program hashes, assuming LinuxHost and LinuxProcessFactory.
for prog in ${TAO_HOSTED_PROGRAMS}; do
if [ -f "$prog" ]; then
proghash=`tao_admin -quiet -getprogramhash "$prog"`
tao_admin -add 'TrustedProgramHash(ext'${proghash}')'
tao_admin -add 'TrustedArgs(ext.Args("'$prog'"))'
tao_admin -add 'TrustedArgs(ext.Args("'$prog'", "-ca=localhost:8124"))'
fi
done
else
for prog in ${TAO_HOSTED_PROGRAMS}; do
if [ -f "$prog" ]; then
tao_admin -canexecute "$prog"
fi
done
fi
tao_admin -show
# TODO(kwalsh) set up fserver user ACLs here.
#tao_admin -newusers tmroeder,jlm
#tao_admin -signacl ${TAO_ROOTDIR}/run/acls.ascii -acl_sig_path user_acls_sig
#mkdir -p file_client_files
#mkdir -p file_server_files
#mkdir -p file_server_meta
echo "Tao configuration is ready"
}
function startsvcs()
{
if pgrep -x `shortname linux_host` >/dev/null; then
echo "LinuxHost service already running";
else
rm -f ${TAO_TEST}/linux_tao_host/admin_socket
linux_host --service &
fi
}
function monitor()
{
echo "Monitoring Tao files..."
(
cd ${TAO_TEST}
while true; do
inotifywait -e modify -e delete -e attrib $watchfiles >/dev/null 2>&1
echo "Files have changed, waiting for quiet..."
sleep 1
while inotifywait -t 3 -e modify -e delete -e attrib $watchfiles >/dev/null 2>&1; do
echo "Still waiting for quiet..."
sleep 1
done
echo "Restarting Tao services..."
refresh
stoptests
startsvcs
done
)
}
function gethash()
{
cat "$1" | sha256sum | cut -d' ' -f1 | xxd -r -ps | base64 | tr '/+' '_-' | tr -d '='
if [ "${PIPESTATUS[0]}" != 0 ]; then false; fi
}
function base64wdecode()
{
cat "$1" | tr "_-" "/+" | base64 -d
if [ "${PIPESTATUS[0]}" != 0 ]; then false; fi
}
function base64wencode()
{
cat "$1" | base64 | tr '/+' '_-' | tr -d '='
if [ "${PIPESTATUS[0]}" != 0 ]; then false; fi
}
function testpgm()
{
cd ${TAO_TEST} # These take no params and must be run within test directory
case "$1" in
client|server)
echo "Starting cloudproxy server..."
server_id=`linux_host -run -- server --v=2`
echo "$server_id";
server_pid=`extract_pid $server_id`
sleep 2
tail -f $GLOG_log_dir/server.INFO &
server_tail_pid=$!
echo "Starting cloudproxy client..."
client_id=`linux_host -run -- client --v=2`
client_pid=`extract_pid $client_id`
sleep 2
tail -f $GLOG_log_dir/client.INFO &
client_tail_pid=$!
sleep 2
echo "Killing cloudproxy server and client..."
kill $server_pid $client_pid 2>/dev/null
sleep 2
kill $server_tail_pid $client_tail_pid 2>/dev/null
;;
file|fclient|fserver)
echo "Clean cloudproxy file server data..."
rm -f file_server_files/* file_server_meta/* file_client_files/*
# make some test data too
echo "test data $RANDOM" >> file_client_files/test
echo "Starting cloudproxy file server..."
server_id=`linux_host -run -- fserver --v=2`
server_pid=`extract_pid $server_id`
sleep 2
tail -f $GLOG_log_dir/fserver.INFO &
server_tail_pid=$!
echo "Starting cloudproxy file client..."
client_id=`linux_host -run -- fclient --v=2`
client_pid=`extract_pid $client_id`
sleep 2
tail -f $GLOG_log_dir/fclient.INFO &
client_tail_pid=$!
sleep 2
echo "Killing cloudproxy file server and client..."
kill $server_pid $client_pid 2>/dev/null
sleep 2
kill $server_tail_pid $client_tail_pid 2>/dev/null
;;
http)
echo "Starting cloudproxy http echo server..."
server_id=`linux_host -run -- http_echo_server --v=2`
server_pid=`extract_pid $server_id`
sleep 2
tail -f $GLOG_log_dir/http_echo_server.INFO &
tail_pid=$!
sleep 1
read -p "Press enter to kill http echo server..."
echo "Killing cloudproxy http echo server..."
kill $server_pid 2>/dev/null
sleep 2
kill $tail_pid 2>/dev/null
;;
https)
echo "Starting cloudproxy https echo server..."
server_id=`linux_host -run -- https_echo_server --v=2`
server_pid=`extract_pid $server_id`
sleep 2
tail -f $GLOG_log_dir/https_echo_server.INFO &
tail_pid=$!
sleep 1
read -p "Press enter to kill https echo server..."
echo "Killing cloudproxy https echo server..."
kill $server_pid 2>/dev/null
sleep 2
kill $tail_pid 2>/dev/null
;;
help|*)
echo "Available test programs:"
echo " demo # a simple demo (run with host.sh)"
echo " server # cloud client/server test (not ported yet)"
echo " fserver # file client/server test (not ported yet)"
echo " http # http echo test (not ported yet)"
echo " https # https echo test (not ported yet)"
;;
esac
}
function hostpgm()
{
prog="$1"
shift
echo "Starting hosted program $prog ..."
prog_id=`linux_host -run -- "$prog" "$@"`
echo "TaoExtension: $prog_id"
}
case "$(basename $0)" in
setup.sh)
stoptests
cleanup
setup
;;
start.sh)
startsvcs
;;
restart.sh)
stoptests
startsvcs
;;
monitor.sh)
monitor
;;
test.sh)
#refresh
#startsvcs
if [ "$#" == "0" ]; then
testpgm help
else
for f in "$@"; do
testpgm $f
done
fi
;;
host.sh)
if [ "$#" == "0" ]; then
echo "usage: $0 [arg...]"
else
hostpgm "$@"
fi
;;
refresh.sh)
refresh
;;
stop.sh)
stoptests
;;
clean.sh)
stoptests
cleanup
;;
hash.sh)
for f in "$@"; do
gethash $f
done
;;
base64w-encode.sh)
for f in "$@"; do
base64wencode $f
done
;;
base64w-decode.sh)
for f in "$@"; do
base64wdecode $f
done
;;
help|*)
cat <... # Run one or more cases. Use test.sh "help" for choices.
host.sh [arg...] # Run with arguments as hosted program.
refresh.sh # Refresh hashes and whitelists, but keep existing keys.
stop.sh # Kill processes, remove logs.
clean.sh # Remove all keys, configuration, logs, etc.
hash.sh [file...] # Hash files; use - for stdin.
base64w-*.sh [file...] # Encode/decode files; use - for stdin.
END
exit 0
;;
esac