#!/usr/bin/env bash

# Define colors uses for status output
RED=`tput setaf 1`
GREEN=`tput setaf 2`
YELLOW=`tput setaf 3`
RESET=`tput sgr0`

CABUNDLE_MAX_ISSUERS=32

function usage () {
  cat <<HELP >&2
Verifies, that custom SSL certificate files are usable
as part of the Katello installation. When passing filenames use absolute paths.

usage: $0 -t [foreman|foreman-proxy] -c CERT_FILE -k KEY_FILE -b CA_BUNDLE_FILE
HELP
}

while getopts "t:c:k:b:" opt; do
    case $opt in
        t)
            TARGET=$(echo $OPTARG|tr '[:upper:]' '[:lower:]')
            ;;
        c)
            CERT_FILE="$(readlink -f $OPTARG)"
            ;;
        k)
            KEY_FILE="$(readlink -f $OPTARG)"
            ;;
        b)
            CA_BUNDLE_FILE="$(readlink -f $OPTARG)"
            ;;
        h)
            usage
            exit 0
            ;;
        ?)
            usage
            exit 1
            ;;
    esac
done

EXIT_CODE=0

if [ -z "$CERT_FILE" -o -z "$KEY_FILE" -o -z "$CA_BUNDLE_FILE" ]; then
    echo 'One of the required parameters is missing.' >&2
    usage
    exit 1
fi

function error () {
    echo -e "\n${RED}[FAIL]${RESET}\n"
    CURRENT_EXIT_CODE=$1
    EXIT_CODE=$((EXIT_CODE|CURRENT_EXIT_CODE))
    echo -e $2 >&2
}

function success () {
    echo -e "\n${GREEN}[OK]${RESET}\n"
}

function warning () {
    echo -e "\n${YELLOW}[WARNING]${RESET}\n"
}

function check-server-cert-encoding () {
    printf 'Checking server certificate encoding: '
    openssl x509 -inform pem -in $CERT_FILE -noout -text &> /dev/null
    if [[ $? == "0" ]]; then
        success
    else
        openssl x509 -inform der -in $CERT_FILE -noout -text &> /dev/null
        if [[ $? == "0" ]]; then
            error 8 "The server certificate is in DER encoding, which is incompatible.\n\n"
            printf "Run the following command to convert the certificate to PEM encoding,\n"
            printf "then test it again.\n"
            printf "# openssl x509 -in %s -outform pem -out %s.pem\n\n" $(basename $CERT_FILE) $(basename $CERT_FILE)
            printf "When you run $(basename $0) again, use file\n"
            printf "%s.pem for the server certificate.\n\n" $(basename $CERT_FILE)
        else
            error 9 "The encoding of the server certificate is unknown."
        fi
    fi
}

function check-expiration () {
    DATE=$(date -u +"%b %-d %R:%S %Y")
    CERT_EXP=$(openssl x509 -noout -enddate -in $CERT_FILE | sed -e 's/notAfter=//' | awk '{$NF="";}1')
    CA_EXP=$(openssl x509 -noout -enddate -in $CA_BUNDLE_FILE | sed -e 's/notAfter=//' | awk '{$NF="";}1')
    DATE_TODAY=$(date -d"${DATE}" +%Y%m%d%H%M%S)
    CERT_DATE=$(date -d"${CERT_EXP}" +%Y%m%d%H%M%S)
    CA_DATE=$(date -d"${CA_EXP}" +%Y%m%d%H%M%S)
    printf "Checking expiration of certificate: "
    if [[ $DATE_TODAY -gt $CERT_DATE ]]; then
        error 6 "The certificate \"$CERT_FILE\" has already expired on: $CERT_EXP"
    else
        success
    fi
    printf "Checking expiration of CA bundle: "
    if [[ $DATE_TODAY -gt $CA_DATE ]]; then
        error 7 "The CA bundle \"$CA_BUNDLE_FILE\" has already expired on: $CA_EXP"
    else
        success
    fi
}

function check-cert-ca-flag () {
    printf "Checking if server certificate has CA:TRUE flag "
    openssl x509 -in $CERT_FILE -noout -text | grep -q CA:TRUE
    if [[ $? -ne 0 ]]; then
        success
    else
        error 7 "The server certificate is marked as a CA and can not be used."
    fi
}

function check-passphrase () {
    printf "Checking for private key passphrase: "
    CHECK=$(cat $KEY_FILE | grep ENCRYPTED)
    if [[ $? == "0" ]]; then
        error 2 "The $KEY_FILE contains a passphrase, remove the key's passphrase by doing:
        \nmv $KEY_FILE $KEY_FILE.old
        \nopenssl rsa -in $KEY_FILE.old -out $KEY_FILE"
        exit 1
    else
        success
    fi
}

function check-priv-key () {
    printf "Checking to see if the private key matches the certificate: "
    CERT_MOD=$(openssl x509 -noout -modulus -in $CERT_FILE)
    KEY_MOD=$(openssl rsa -noout -modulus -in $KEY_FILE)
    if [[ "$CERT_MOD" != "$KEY_MOD" ]]; then
        error 2 "The $KEY_FILE does not match the $CERT_FILE"
    else
        success
    fi
}

function check-ca-bundle () {
    printf "Checking CA bundle against the certificate file: "
    CHECK=$(openssl verify -CAfile $CA_BUNDLE_FILE -purpose sslserver -verbose $CERT_FILE 2>&1)
    if [[ $? == "0" ]]; then
        success
    else
        error 4  "The $CA_BUNDLE_FILE does not verify the $CERT_FILE"
        echo $CHECK
    fi
}

function check-ca-bundle-size () {
    printf "Checking CA bundle size: "
    CHECK=$(grep -c "^--*BEGIN" $CA_BUNDLE_FILE)
    if [[ $CHECK -lt $CABUNDLE_MAX_ISSUERS ]]; then
		success
    else
		CERTISSUER=$(openssl x509 -noout -in $CERT_FILE -issuer 2>&1)
		error 10 "The CA bundle counts $CHECK issuers. Please trim your CA bundle and include only the certs relevant to your cert file"
		echo $CERTISSUER
		echo
    fi
}

function check-cert-san () {
    printf "Checking Subject Alt Name on certificate "
    CHECK=$(openssl x509 -noout -text -in $CERT_FILE | grep DNS:)
    if [[ $? == "0" ]]; then
        success
    else
        warning
        echo "The $CERT_FILE does not contain a Subject Alt Name."
    fi
}

function check-cert-usage-key-encipherment () {
    printf "Checking Key Usage extension on certificate for Key Encipherment "
    CHECK=$(openssl x509 -noout -text -in $CERT_FILE | grep -A1 'X509v3 Key Usage:' | grep 'Key Encipherment')
    if [[ $? == "0" ]]; then
        success
    else
        error 4 "The $CERT_FILE does not allow for the 'Digital Signature' key usage."
    fi
}

check-server-cert-encoding
check-expiration
check-cert-ca-flag
check-passphrase
check-priv-key
check-ca-bundle
check-ca-bundle-size
check-cert-san
check-cert-usage-key-encipherment

if [[ $EXIT_CODE == "0" ]] && ([[ $TARGET == "foreman" ]] || [[ -z "$TARGET" ]]) ; then
    echo -e "${GREEN}Validation succeeded${RESET}\n"
    cat <<EOF

To install the Katello main server with the custom certificates, run:

    foreman-installer --scenario katello \\
                      --certs-server-cert "$(readlink -f $CERT_FILE)" \\
                      --certs-server-key "$(readlink -f $KEY_FILE)" \\
                      --certs-server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)"

To update the certificates on a currently running Katello installation, run:

    foreman-installer --scenario katello \\
                      --certs-server-cert "$(readlink -f $CERT_FILE)" \\
                      --certs-server-key "$(readlink -f $KEY_FILE)" \\
                      --certs-server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)" \\
                      --certs-update-server --certs-update-server-ca

To use them inside a NEW \$FOREMAN_PROXY, rerun this command with -t foreman-proxy
EOF
elif [[ $EXIT_CODE == "0" ]]; then
  echo -e "${GREEN}Validation succeeded${RESET}\n"
  cat <<EOF

  To use them inside a NEW \$FOREMAN_PROXY, run this command:

      foreman-proxy-certs-generate --foreman-proxy-fqdn "\$FOREMAN_PROXY" \\
                                   --certs-tar  "~/\$FOREMAN_PROXY-certs.tar" \\
                                   --server-cert "$(readlink -f $CERT_FILE)" \\
                                   --server-key "$(readlink -f $KEY_FILE)" \\
                                   --server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)" \\

  To use them inside an EXISTING \$FOREMAN_PROXY, run this command INSTEAD:

      foreman-proxy-certs-generate --foreman-proxy-fqdn "\$FOREMAN_PROXY" \\
                                   --certs-tar  "~/\$FOREMAN_PROXY-certs.tar" \\
                                   --server-cert "$(readlink -f $CERT_FILE)" \\
                                   --server-key "$(readlink -f $KEY_FILE)" \\
                                   --server-ca-cert "$(readlink -f $CA_BUNDLE_FILE)" \\
                                   --certs-update-server
EOF
else
  exit $EXIT_CODE
fi
