#!/bin/bash
#
# Copyright (c) Citrix Systems 2007, 2008, 2017. All rights reserved.
#
# Generate a TLS certificate file to use with stunnel etc.
#
set -e
FILE=$1
CN=$2
FORCE=$3
set -eu

if [ -z "${CN}" ] || [ "${CN}" = "--force" ] || [ -z "${FILE}" ] || [ "${FILE}" = "--force" ]; then
	echo "usage: $0 <certname> <cn> [--force]"
	exit 2
fi

if [ -e "${FILE}" ] && [ "${FORCE}" != "--force" ]; then
	echo "file already exists: cannot overwrite";
	exit 3
fi

OVERWRITE_PEM=0
if [ "${FORCE}" = "--force" ]; then
  OVERWRITE_PEM=1
fi

dnsnames=$( (hostname -A; hostname -f) | sort | uniq)

DIR=$(mktemp -d tls-cert-generation-XXXXXXXXXX --tmpdir)

function cleanup {
	rm -rf "${DIR}"
}
trap cleanup ERR EXIT

pushd "${DIR}"

# config file written so as to give the same behaviour with the
# openssl "req" and "x509" commands (because x509 fails to include
# extension sections when creating a certificate from a CSR).
cat <<@eof >config
extensions = ext_section # For openssl x509 command
[req] # openssl req params
prompt = no
distinguished_name = dn-param
req_extensions = ext_section
[dn-param] # DN fields
CN = ${CN}
[ ext_section ]
subjectAltName = @alt_names
[alt_names]
@eof

i=1
for x in $dnsnames
do
	echo "DNS.${i}=${x}" >> config
	(( i++ ))
done

openssl genrsa 2048 > privkey.rsa

# Generate certificate-signing-request
openssl req -batch -new -key privkey.rsa -days 3650 -config config -out cert.csr

# Sign it, creating a certificate.
# Due to a bug in openssl x509, the extensions in the CSR are ignored
# so we have to re-specify them using the -extfile option.
# (Trying to do this in one step by passing -x509 to the "req" command ignores
# the extensions too, but "req" does not allow the -extfile option.)
openssl x509 -extfile config -req -signkey privkey.rsa -in cert.csr -outform PEM -out signedpubcert.pem -days 3650

popd

tmpcert=$(mktemp -p "${DIR}" cert-XXXXXXXXXX.pem.tmp)

(cat "${DIR}/privkey.rsa" <(echo) "${DIR}/signedpubcert.pem") > "${tmpcert}"

# Set up tmpcert the way we want it, then use mv to ensure that FILE
# appears as an atomic event.
chmod 400 "${tmpcert}"

if [ "${OVERWRITE_PEM}" -eq 0 ]; then
  mv --no-clobber "${tmpcert}" "${FILE}"
elif [ "${OVERWRITE_PEM}" -eq 1 ]; then
  mv --force "${tmpcert}" "${FILE}"
fi

# Now check whether the mv succeeded (since it returns a 0 exit-code even if FILE existed already).
if [ -e "${tmpcert}" ]; then
	# It still exists in original location: it has not been moved.
	echo "File has been created just now by someone else: cannot overwrite";
	exit 3 # Rely on the EXIT trap to clean up.
fi

exit 0 # Rely on the EXIT trap to clean up.
