mirror of
https://github.com/stefanberger/swtpm.git
synced 2026-08-07 19:51:51 +00:00
Using the kernel name was not a good choice for determining which flavor (BSD vs. GNU) of stat, sed, and install was being used since one could install either one of them at least on MacOS. Instead, check whether --help is supported by these tools. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
421 lines
9.6 KiB
Bash
Executable File
421 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
FLAG_OVERWRITE=1
|
|
FLAG_TPM2=2
|
|
|
|
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 stat --help &>/dev/null; then
|
|
stat -c%s "$1"
|
|
else
|
|
# OpenBSD
|
|
stat -f%z "$1"
|
|
fi
|
|
}
|
|
|
|
# Get the OpenSSL provider path for the given module searching a few well-known
|
|
# paths.
|
|
function get_provider_path()
|
|
{
|
|
local module="$1"
|
|
|
|
local p
|
|
|
|
for p in \
|
|
"/usr/lib64/ossl-modules" \
|
|
"/usr/lib/ossl-modules" \
|
|
"/usr/lib/$(uname -m)-linux-gnu/ossl-modules";
|
|
do
|
|
if [ -r "${p}/${module}" ]; then
|
|
echo "${p}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
logerr "Could not find OpenSSL provider path for ${module} module."
|
|
return 1
|
|
}
|
|
|
|
# Create a config value by escaping the proper characters
|
|
#
|
|
# @param 1: The string to escape
|
|
function escape_pkcs11_url()
|
|
{
|
|
echo "${1//;/\\;}"
|
|
}
|
|
|
|
create_localca_cert() {
|
|
local flags=$1
|
|
local dir="$2"
|
|
local outfile="$3"
|
|
local owner="$4"
|
|
local pid="$5" # TPM2 parameter
|
|
local algorithm="$6" # RSA or EC-key signing
|
|
|
|
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 pinfile=${dir}/pin
|
|
local passfile=${dir}/password
|
|
local tpmkeyurl
|
|
local msg output providerpath
|
|
|
|
export SWTPM_ROOTCA_PASSWORD
|
|
|
|
if ! [ -r "${cakey}" ] || ! [ -r "${cacert}" ]; then
|
|
# shellcheck disable=2086
|
|
if ! msg=$(openssl \
|
|
req \
|
|
-x509 \
|
|
-keyout "${cakey}" \
|
|
-newkey rsa:3072 \
|
|
${SWTPM_ROOTCA_PASSWORD:+-passout "env:SWTPM_ROOTCA_PASSWORD"} \
|
|
${SWTPM_ROOTCA_PASSWORD:--noenc} \
|
|
-out "${cacert}" \
|
|
-days 36500 \
|
|
-sha256 \
|
|
-subj "/CN=swtpm-localca-rootca" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign" \
|
|
2>&1);
|
|
then
|
|
logerr "Could not create root CA."
|
|
logerr "${msg}"
|
|
rm -f "${cakey}" "${passfile}"
|
|
return 1
|
|
fi
|
|
chmod 640 "${cakey}"
|
|
rm -f "${passfile}"
|
|
else
|
|
logit "Reusing existing root CA"
|
|
fi
|
|
|
|
rm -f "${tpmkey}" "${tpmpubkey}" "${tpmca}"
|
|
|
|
if [ $((flags & FLAG_TPM2)) -ne 0 ]; then
|
|
local tokenurl tpmkeyurl
|
|
local token="swtpm-tpmca-${pid}"
|
|
local label="${token}" # must be same
|
|
local keylabel="swtpm-tpmca-key"
|
|
local userpin="${SWTPM_PKCS11_PIN:-swtpm-tpmca}"
|
|
|
|
tokenurl=$(p11tool --list-tokens 2>&1 | \
|
|
grep -E ";token=${token}\$" | \
|
|
sed -n "s/.*URL: //p")
|
|
if [ -z "${tokenurl}" ]; then
|
|
if [ -z "${SWTPM_PKCS11_SO_PIN}" ]; then
|
|
logerr "The env. variable SWTPM_PKCS11_SO_PIN must be set to create token ${label}."
|
|
return 1
|
|
fi
|
|
if ! msg=$(tpm2_ptool addtoken \
|
|
--pid "${pid}" \
|
|
--sopin "${SWTPM_PKCS11_SO_PIN}" \
|
|
--userpin "${userpin}" \
|
|
--label "${label}" 2>&1);
|
|
then
|
|
logerr "Error: Could not create pkcs11 token"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
tokenurl=$(p11tool --list-tokens 2>&1 | \
|
|
grep -E ";token=${token}\$" | \
|
|
sed -n "s/.*URL: //p")
|
|
if [ -z "${tokenurl}" ]; then
|
|
logerr "Error: Could not get token URL for token '${token}'"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
if ! msg=$(tpm2_ptool config \
|
|
--key tcti \
|
|
--value tabrmd \
|
|
--label "${label}");
|
|
then
|
|
logerr "Error: Could not set config value for tcti key"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
export GNUTLS_PIN="${userpin}"
|
|
# GNUTLS_SO_PIN not needed at this point
|
|
|
|
if msg="$(p11tool --login --list-keys "${tokenurl}" 2>&1)"; then
|
|
tpmkeyurl=$(echo "${msg}" | \
|
|
grep ";object=${keylabel}" | \
|
|
sed -n "s/.*URL: //p")
|
|
fi
|
|
if [ -z "${tpmkeyurl}" ]; then
|
|
if ! msg=$(tpm2_ptool addkey \
|
|
"--label=${label}" \
|
|
"--userpin=${userpin}" \
|
|
"--algorithm=${algorithm}" \
|
|
"--key-label=${keylabel}" \
|
|
--id 1 2>&1);
|
|
then
|
|
logerr "Error: Could not create create key under pkcs11 token ${token}"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
if ! msg="$(p11tool --login --list-keys "${tokenurl}" 2>&1)"; then
|
|
logerr "Error: Could not get TPM key URL for ${tokenurl}"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
tpmkeyurl=$(echo "${msg}" | \
|
|
grep ";object=${keylabel}" | \
|
|
sed -n "s/.*URL: //p")
|
|
if [ -z "${tpmkeyurl}" ]; then
|
|
logerr "Error: Could not get TPM key URL for ${tokenurl}"
|
|
logerr "${msg}"
|
|
return 1
|
|
fi
|
|
fi
|
|
rm -f "${tpmpubkey}"
|
|
|
|
if ! msg=$(p11tool --export-pubkey "${tpmkeyurl}" --login --outfile "${tpmpubkey}" 2>&1) || \
|
|
[ ! -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
|
|
else
|
|
logerr "TPM1.2 is not supported anymore."
|
|
return 1
|
|
fi
|
|
|
|
if ! providerpath=$(get_provider_path "pkcs11.so"); then
|
|
return 1
|
|
fi
|
|
|
|
# Write any PIN into a PIN file
|
|
echo "${SWTPM_PKCS11_PIN}" > "${pinfile}"
|
|
if ! msg=$(openssl \
|
|
req \
|
|
-provider-path "${providerpath}" \
|
|
-provider pkcs11 \
|
|
-x509 \
|
|
-key "${tpmkeyurl//%00/}${SWTPM_PKCS11_PIN:+?pin-source=${pinfile}}" \
|
|
-out "${tpmca}" \
|
|
-days 36500 \
|
|
-sha256 \
|
|
-CA "${cacert}" \
|
|
-CAkey "${cakey}" \
|
|
${SWTPM_ROOTCA_PASSWORD:+-passin "env:SWTPM_ROOTCA_PASSWORD"} \
|
|
-subj "/CN=swtpm-localca" \
|
|
-addext "basicConstraints=critical,CA:TRUE" \
|
|
-addext "keyUsage=critical,keyCertSign" \
|
|
2>&1);
|
|
then
|
|
logerr "Could not create TPM CA"
|
|
logerr "${msg}"
|
|
rm -f "${pinfile}"
|
|
return 1
|
|
fi
|
|
rm -f "${pinfile}"
|
|
|
|
output="statedir = ${dir}
|
|
signingkey = $(escape_pkcs11_url "${tpmkeyurl//%00/}")
|
|
issuercert = ${tpmca}
|
|
certserial = ${dir}/certserial"
|
|
|
|
output+="$(echo -e "\nSWTPM_PKCS11_PIN = ${SWTPM_PKCS11_PIN}")"
|
|
# output+="$(echo -e "\nSWTPM_PKCS11_SO_PIN = ${SWTPM_PKCS11_SO_PIN}")"
|
|
if [ -n "${TPM_KEY_PASSWORD}" ]; then
|
|
output+="$(echo -e "\nsigningkey_password = ${TPM_KEY_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}"
|
|
|
|
if pushd "${dir}" &>/dev/null; then
|
|
chown "${owner}:${group}" ./*
|
|
popd &>/dev/null || return 1
|
|
fi
|
|
|
|
if [ -n "${outfile}" ]; then
|
|
chown "${owner}:${group}" "${outfile}"
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
} #create_localca_cert
|
|
|
|
usage() {
|
|
local flags=$2
|
|
|
|
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
|
|
--key-password s Password for the newly created TPM key
|
|
--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'
|
|
--tpm2 Setup a CA that uses a TPM 2.0
|
|
--algorithm <alg> Key algorithm for created TPM 2 CA. Default is rsa2048.
|
|
Possible values are: rsa2048, rsa3072, ecc256 or secp256r1,
|
|
ecc384 or secp384r1
|
|
--pid <pid> Pimary object Id used by tpm2_ptool; only valid if --tpm2
|
|
is used
|
|
--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
|
|
|
|
main() {
|
|
local flags=0
|
|
local dir outfile owner group msg pid
|
|
local algorithm="rsa2048"
|
|
|
|
while [ $# -ne 0 ]; do
|
|
case "$1" in
|
|
--dir)
|
|
shift
|
|
dir="$1"
|
|
;;
|
|
--overwrite)
|
|
flags=$((flags | FLAG_OVERWRITE))
|
|
;;
|
|
--key-password)
|
|
shift
|
|
TPM_KEY_PASSWORD="$1"
|
|
;;
|
|
--outfile)
|
|
shift
|
|
outfile="$1"
|
|
;;
|
|
--owner)
|
|
shift
|
|
owner="$1"
|
|
;;
|
|
--group)
|
|
shift
|
|
group="$1"
|
|
;;
|
|
--tpm2)
|
|
flags=$((flags | FLAG_TPM2))
|
|
;;
|
|
--algorithm)
|
|
shift
|
|
algorithm="$1"
|
|
;;
|
|
--pid)
|
|
shift
|
|
pid="$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 [ $((flags & FLAG_TPM2)) -ne 0 ] && [ -z "${pid}" ]; then
|
|
logerr "--pid is required for TPM 2"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
if [ -n "${owner}" ]; then
|
|
if ! msg="$(id -u "${owner}" 2>&1)"; then
|
|
logerr "User ${owner} cannot be used: ${msg}"
|
|
return 1
|
|
fi
|
|
else
|
|
owner="root"
|
|
fi
|
|
if [ -n "${group}" ]; then
|
|
if ! msg="$(id -g "${group}" 2>&1)"; then
|
|
logerr "Group ${group} cannot be used: ${msg}"
|
|
return 1
|
|
fi
|
|
else
|
|
group="root"
|
|
fi
|
|
fi
|
|
|
|
if ! mkdir -p "${dir}"; then
|
|
logerr "Could not create directory ${dir}."
|
|
return 1
|
|
fi
|
|
|
|
if ! [[ "${algorithm}" =~ ^(rsa2048|rsa3072|ecc256|ecc384|secp256r1|secp384r1)$ ]]; then
|
|
logerr "Unsupported key algorithm for TPM CA '${algorithm}'. See --help."
|
|
return 1
|
|
else
|
|
case "${algorithm}" in
|
|
secp256r1) algorithm=ecc256;;
|
|
secp384r1) algorithm=ecc384;;
|
|
esac
|
|
fi
|
|
|
|
create_localca_cert "${flags}" "${dir}" "${outfile}" "${owner}" "${pid}" "${algorithm}"
|
|
return $?
|
|
} #main
|
|
|
|
main "$@"
|
|
exit $?
|