mirror of
https://github.com/stefanberger/swtpm.git
synced 2026-01-01 04:45:30 +00:00
If tpmtool supports --srk-well-known we also support the well known SRK password and allow the user not to provide an SRK password on the command line. This patch should have been applied before the previous patch that tests this. Luckily, only very few systems have tpmtool with --srk-well-known so that the order would matter there (and cause test failures). Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
422 lines
9.5 KiB
Bash
Executable File
422 lines
9.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
FLAG_OVERWRITE=1
|
|
FLAG_REGISTER_KEY=2
|
|
FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN=4
|
|
FLAG_SRK_WELL_KNOWN=8
|
|
|
|
TSS_TCSD_HOSTNAME_DEFAULT=localhost
|
|
TSS_TCSD_PORT_DEFAULT=30003
|
|
|
|
logit()
|
|
{
|
|
if [ -z "$LOGFILE" ]; then
|
|
echo "$@" >&1
|
|
else
|
|
echo "$@" >> $LOGFILE
|
|
fi
|
|
}
|
|
|
|
logerr()
|
|
{
|
|
if [ -z "$LOGFILE" ]; then
|
|
echo "Error: $@" >&2
|
|
else
|
|
echo "Error: $@" >> $LOGFILE
|
|
fi
|
|
}
|
|
|
|
# Get the size of a file in bytes
|
|
#
|
|
# @1: filename
|
|
function get_filesize()
|
|
{
|
|
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
|
|
stat -c%s $1
|
|
else
|
|
# OpenBSD
|
|
stat -f%z $1
|
|
fi
|
|
}
|
|
|
|
# Use expect for automating the interaction with the tpmtool
|
|
#
|
|
# @param 1...: parameters to pass to tpmtool command line
|
|
#
|
|
# TPM_SRK_PASSWORD and TPM_KEY_PASSWORD global variables are used
|
|
# for the SRK and key passwords respectively.
|
|
run_tpmtool() {
|
|
local prg out rc
|
|
|
|
prg="spawn tpmtool $@
|
|
expect {
|
|
\"Enter SRK password:\" {
|
|
send \"${TPM_SRK_PASSWORD}\n\"
|
|
exp_continue
|
|
}
|
|
\"Enter key password:\" {
|
|
send \"${TPM_KEY_PASSWORD}\n\"
|
|
exp_continue
|
|
}
|
|
\"tpmkey:\" {
|
|
send_user \"\n\"
|
|
}
|
|
eof {
|
|
exit
|
|
}
|
|
}
|
|
catch wait result
|
|
exit [lindex \$result 3]
|
|
"
|
|
out=$(expect -c "${prg}")
|
|
rc=$?
|
|
echo "${out}"
|
|
return $rc
|
|
} #run_tpmtool
|
|
|
|
create_localca_cert() {
|
|
local flags=$1
|
|
local dir="$2"
|
|
local outfile="$3"
|
|
local owner="$4"
|
|
|
|
local cakey=${dir}/swtpm-localca-rootca-privkey.pem
|
|
local cacert=${dir}/swtpm-localca-rootca-cert.pem
|
|
local tpmkey=${dir}/swtpm-localca-tpmca-privkey.pem
|
|
local tpmpubkey=${dir}/swtpm-localca-tpmca-pubkey.pem
|
|
local tpmca=${dir}/swtpm-localca-tpmca-cert.pem
|
|
local template=${dir}/template
|
|
local tpmkeyurl params
|
|
local msg output
|
|
|
|
if ! [ -r "${cakey}" ] || ! [ -r ${cacert} ]; then
|
|
local passparam
|
|
|
|
if [ -n "${SWTPM_ROOTCA_PASSWORD}" ]; then
|
|
passparam="--password ${SWTPM_ROOTCA_PASSWORD}"
|
|
fi
|
|
msg=$(${CERTTOOL} \
|
|
--generate-privkey \
|
|
--outfile ${cakey} \
|
|
${passparam} \
|
|
2>&1)
|
|
[ $? -ne 0 ] && {
|
|
logerr "Could not create root-CA key ${cakey}."
|
|
logerr "${msg}"
|
|
return 1
|
|
}
|
|
chmod 640 ${cakey}
|
|
|
|
echo "cn=swtpm-localca-rootca" > ${template}
|
|
echo "ca" >> ${template}
|
|
echo "cert_signing_key" >> ${template}
|
|
echo "expiration_days = 3650" >> ${template}
|
|
|
|
msg=$(GNUTLS_PIN=${SWTPM_ROOTCA_PASSWORD} ${CERTTOOL} \
|
|
--generate-self-signed \
|
|
--template ${template} \
|
|
--outfile ${cacert} \
|
|
--load-privkey ${cakey} \
|
|
2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Could not create root CA."
|
|
logerr "${msg}"
|
|
rm -f ${cakey} ${template}
|
|
return 1
|
|
fi
|
|
else
|
|
logit "Reusing existing root CA"
|
|
fi
|
|
|
|
rm -f ${tpmkey} ${tpmpubkey} ${tpmca}
|
|
|
|
if [ $((flags & FLAG_SRK_WELL_KNOWN)) -ne 0 ]; then
|
|
unset GNUTLS_PIN
|
|
params="--srk-well-known"
|
|
else
|
|
export GNUTLS_PIN=${TPM_SRK_PASSWORD}
|
|
fi
|
|
|
|
if [ $((flags & FLAG_REGISTER_KEY)) -ne 0 ]; then
|
|
msg="$(run_tpmtool --generate-rsa --signing --register ${params})"
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Could not generate registered signing key with tpmtool"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p')
|
|
if [ -z "${tpmkeyurl}" ]; then
|
|
logerr "Could not parse tpmkey URL"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
else
|
|
rm -f "${tpmkey}"
|
|
msg="$(run_tpmtool --generate-rsa --signing --outfile "${tpmkey}" ${params})"
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Could not create signing key with tpmtool"
|
|
logerr "${msg}"
|
|
rm -f "${tpmkey}"
|
|
return 1
|
|
fi
|
|
if [ ! -r "${tpmkey}" ] || [ $(get_filesize "${tpmkey}") -eq 0 ]; then
|
|
logerr "The TPM key file ${tpmkey} was not written properly"
|
|
rm -f "${tpmkey}"
|
|
return 1
|
|
fi
|
|
chmod 640 ${tpmkey}
|
|
tpmkeyurl=tpmkey:file=${tpmkey}
|
|
fi
|
|
|
|
rm -f "${tpmpubkey}"
|
|
msg=$(run_tpmtool --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}" ${params})
|
|
if [ $? -ne 0 ] || \
|
|
[ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then
|
|
logerr "Error: Could not get TPM public key"
|
|
logerr "${msg}"
|
|
rm -f "${tpmkey}" "${tpmpubkey}"
|
|
return 1
|
|
fi
|
|
|
|
echo "cn=swtpm-localca" > ${template}
|
|
echo "ca" >> ${template}
|
|
echo "cert_signing_key" >> ${template}
|
|
echo "expiration_days = 3650" >> ${template}
|
|
|
|
msg=$(${CERTTOOL} \
|
|
--generate-certificate \
|
|
--template ${template} \
|
|
--outfile ${tpmca} \
|
|
--load-ca-privkey ${cakey} \
|
|
--load-ca-certificate ${cacert} \
|
|
--load-privkey ${tpmkeyurl} \
|
|
--load-pubkey ${tpmpubkey} \
|
|
2>&1)
|
|
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Could not create TPM CA"
|
|
logerr "${msg}"
|
|
rm -f "${template}"
|
|
return 1
|
|
fi
|
|
|
|
output="statedir = ${dir}
|
|
signingkey = ${tpmkeyurl}
|
|
issuercert = ${tpmca}
|
|
certserial = ${dir}/certserial
|
|
TSS_TCSD_HOSTNAME = ${TSS_TCSD_HOSTNAME}
|
|
TSS_TCSD_PORT = ${TSS_TCSD_PORT}"
|
|
|
|
if [ -n "${TPM_KEY_PASSWORD}" ]; then
|
|
output+="$(echo -e "\nsigningkey_password = ${TPM_KEY_PASSWORD}")"
|
|
fi
|
|
if [ -n "${TPM_SRK_PASSWORD}" ]; then
|
|
output+="$(echo -e "\nparentkey_password = ${TPM_SRK_PASSWORD}")"
|
|
fi
|
|
|
|
if [ -n "${outfile}" ]; then
|
|
echo "${output}" > "${outfile}"
|
|
chmod 640 "${outfile}"
|
|
fi
|
|
echo "${output}"
|
|
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
chown "${owner}":"${group}" ${dir}
|
|
|
|
pushd ${dir} &>/dev/null
|
|
if [ $? -eq 0 ]; then
|
|
chown "${owner}":"${group}" *
|
|
popd &>/dev/null
|
|
fi
|
|
|
|
if [ -n "${outfile}" ]; then
|
|
chown "${owner}":"${group}" "${outfile}"
|
|
fi
|
|
fi
|
|
|
|
rm -f ${template}
|
|
|
|
return 0
|
|
} #create_localca_cert
|
|
|
|
usage() {
|
|
local flags=$2
|
|
|
|
local tpmtool_note=" use 'well known' password if not
|
|
given"
|
|
[ $((flags & FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN)) -eq 0 ] && \
|
|
tpmtool_note="
|
|
Note: the well known password of 20 zero bytes is not
|
|
supported by tpmtool"
|
|
|
|
cat << _EOF_
|
|
Create a TPM-based CA for signing EK and platform certificates.
|
|
|
|
Usage: $(basename $1) [options]
|
|
|
|
THIS SCRIPT IS EXPERIMENTAL
|
|
|
|
The following options are supported:
|
|
|
|
--dir directory Directory where to write the CA files into; must not exist
|
|
unless --overwrite is passed
|
|
--overwrite Overwrite any data in an existing directory; tries to
|
|
reuse a root CA if one is found there
|
|
--register Create a registered TPM 1.2 key rather than a file that
|
|
contains the key
|
|
--key-password s Password for the newly created TPM key; required if
|
|
--register is not passed
|
|
Note: use the same as the --srk-password (bug in certtool)
|
|
--srk-password s Password for the TPM's SRK;${tpmtool_note}
|
|
--outfile file File to write the configuration to; if not passed it will be
|
|
written to stdout only
|
|
--owner owner The owner of the directory and the files; only set if this
|
|
script is run as root; recommended to be 'tss'
|
|
--group group The group owning the directory and the files;
|
|
recommended to be 'tss'
|
|
--tss-tcsd-hostname hostname
|
|
The name of the host where tcsd (TrouSerS daemon) is running
|
|
on; default is '${TSS_TCSD_HOSTNAME_DEFAULT}'
|
|
--tss-tcsd-port p The TCP port on which tcsd is listening for connections;
|
|
default is ${TSS_TCSD_PORT_DEFAULT}
|
|
--help, -h, -? Display this help screen and exit
|
|
|
|
|
|
The following environment variables are supported:
|
|
|
|
SWTPM_ROOTCA_PASSWORD The root CA's private key password
|
|
|
|
_EOF_
|
|
} #usage
|
|
|
|
# Check whether tpmtool supports --srk-well-known
|
|
tpmtool_supports_srk_well_known()
|
|
{
|
|
local tmp
|
|
|
|
tmp=$(tpmtool --help | grep "srk-well-known")
|
|
[ -z "${tmp}" ] && return 1
|
|
return 0
|
|
}
|
|
|
|
main() {
|
|
local flags=0
|
|
local dir outfile owner group msg
|
|
|
|
if tpmtool_supports_srk_well_known; then
|
|
flags=$((flags | FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN | FLAG_SRK_WELL_KNOWN))
|
|
fi
|
|
|
|
CERTTOOL=certtool
|
|
export TSS_TCSD_HOSTNAME=${TSS_TCSD_HOSTNAME_DEFAULT}
|
|
export TSS_TCSD_PORT=${TSS_TCSD_PORT_DEFAULT}
|
|
|
|
while [ $# -ne 0 ]; do
|
|
case "$1" in
|
|
--dir)
|
|
shift
|
|
dir="$1"
|
|
;;
|
|
--overwrite)
|
|
flags=$((flags | FLAG_OVERWRITE))
|
|
;;
|
|
--register)
|
|
flags=$((flags | FLAG_REGISTER_KEY))
|
|
;;
|
|
--srk-password)
|
|
shift
|
|
TPM_SRK_PASSWORD="$1"
|
|
flags=$((flags & ~FLAG_SRK_WELL_KNOWN))
|
|
;;
|
|
--key-password)
|
|
shift
|
|
TPM_KEY_PASSWORD="$1"
|
|
;;
|
|
--outfile)
|
|
shift
|
|
outfile="$1"
|
|
;;
|
|
--owner)
|
|
shift
|
|
owner="$1"
|
|
;;
|
|
--group)
|
|
shift
|
|
group="$1"
|
|
;;
|
|
--tss-tcsd-hostname)
|
|
shift
|
|
TSS_TCSD_HOSTNAME="$1"
|
|
;;
|
|
--tss-tcsd-port)
|
|
shift
|
|
TSS_TCSD_PORT="$1"
|
|
;;
|
|
--help|-h|-?)
|
|
usage "$0" "${flags}"
|
|
exit 0
|
|
;;
|
|
*)
|
|
logerr "Unsupported option $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
if [ -z "${dir}" ]; then
|
|
logerr "Missing --dir option."
|
|
return 1
|
|
fi
|
|
# strip trailing '/' from dir
|
|
dir="$(echo "${dir}" | sed -n 's|[/]*$||p')"
|
|
|
|
if [ -d "${dir}" ] && [ $((flags & FLAG_OVERWRITE)) -eq 0 ]; then
|
|
logerr "Refusing to overwrite existing directory ${dir}."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${TPM_SRK_PASSWORD}" ] && \
|
|
[ $((flags & FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN)) -eq 0 ]; then
|
|
logerr "SRK password must be provided"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${TPM_KEY_PASSWORD}" ] && [ $((flags & FLAG_REGISTER_KEY)) -eq 0 ]; then
|
|
logerr "Key password is required"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
if [ -n "${owner}" ]; then
|
|
msg="$(id -u "${owner}" 2>&1)"
|
|
if [ $? -ne 0 ]; then
|
|
logerr "User ${owner} cannot be used: ${msg}"
|
|
return 1
|
|
fi
|
|
else
|
|
owner="root"
|
|
fi
|
|
if [ -n "${group}" ]; then
|
|
msg="$(id -g "${group}" 2>&1)"
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Group ${group} cannot be used: ${msg}"
|
|
return 1
|
|
fi
|
|
else
|
|
group="root"
|
|
fi
|
|
fi
|
|
|
|
mkdir -p "${dir}"
|
|
if [ $? -ne 0 ]; then
|
|
logerr "Could not create directory ${dir}."
|
|
return 1
|
|
fi
|
|
|
|
create_localca_cert "${flags}" "${dir}" "${outfile}" "${owner}"
|
|
return $?
|
|
} #main
|
|
|
|
main "$@"
|
|
exit $? |