#!/bin/bash
#
# Program: Expiration Check for Cryptographic Keys and Certificates
# <crypt-expiry-check>
#
# Author of ssl-cert-check: Matty < matty91 at gmail dot com >
# Maintainer of crypt-expiry-check: Erich < crux at eckner dot net >
#
# Purpose:
#  crypt-expiry-check checks to see if a digital certificate in X.509 format
#  or a GnuPG-key has expired. ssl-cert-check can be run in interactive
#  and batch mode, and provides facilities to alarm if a certificate is
#  about to expire.
#
# License:
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#  GNU General Public License for more details.
#
# Requirements:
#   Requires openssl gnupg
#
# Installation:
#   Copy the shell script to a suitable location
#
# Usage:
#  Refer to the usage() sub-routine, or invoke crypt-expiry-check
#  with the "-h" option.

PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/ssl/bin:/usr/sfw/bin
export PATH

# Who to page when an expired certificate is detected (cmdline: -e)
ADMIN="root"

# Number of days in the warning threshhold  (cmdline: -x)
WARNDAYS=30

# If QUIET is set to true, don't print anything on the console (cmdline: -q)
QUIET=false

# Don't send E-mail by default (cmdline: -a)
ALARM=false

# Don't run as a Nagios plugin by default (cmdline: -n)
NAGIOS=false

# Don't print issuer by default
ISSUER=false

# Print header by default
NOHEADER=false

# Do not validate by default
VALIDATION=false

# NULL out the PKCSDBPASSWD variable for later use (cmdline: -k)
PKCSDBPASSWD=""

# Type of certificate (PEM, DER, NET) (cmdline: -t)
CERTTYPE="pem"

# Protocol version to use (cmdline: -v)
VERSION=""

# Enable debugging
DEBUG=false

# Location of system binaries
AWK=$(which awk)
DATE=$(which date)
GREP=$(which grep)
OPENSSL=$(which openssl)
PRINTF=$(which printf)
SED=$(which sed)
TEE=$(which tee)
TR=$(which tr)
SORT=$(which sort)
TAIL=$(which tail)
MKTEMP=$(which mktemp)
GPG=$(which gpg)
HOSTNAME=$(uname -n)

# Try to find a mail client
MAIL="cantfindit"
for prefix in /usr/bin /bin /usr/sbin /sbin
do
    for executable in sendmailadvanced sendmail mailx mail
    do
        [ "${MAIL}" == "cantfindit" ] && [ -f "${prefix}/${executable}" ] && MAIL="${prefix}/${executable}"
    done
done

# Return code used by nagios. Initialize to 0.
RETCODE=0

# Set the default umask to be somewhat restrictive
umask 077

#####################################################################
# Purpose: set RETCODE at least to the given value
# Arguments:
#   $1 -> minimal RETCODE
#####################################################################
set_retcode()
{
    [ ${RETCODE} -lt $1 ] && RETCODE=$1
}

format_date()
{
    if echo "$*" | ${GREP} -qx '[0-9]\+'; then
        ${DATE} '+%F' -d@"$*"
    elif echo "$*" | ${GREP} -qx '[0-9]\+-[0-9]\+-[0-9]\+'; then
        ${DATE} '+%F' -d"$*"
    else
        ${DATE} -d"$(echo "$*" | ${AWK} '{ print $1, $2, $4 }')" +%F
    fi
}

#####################################################################
# Purpose: Print a line with the expiraton interval
# Arguments:
#   $1 -> Hostname
#   $2 -> TCP Port
#   $3 -> Status of certification (e.g., expired or valid)
#   $4 -> Date when certificate will expire
#   $5 -> Days left until the certificate will expire
#   $6 -> Issuer of the certificate
#####################################################################
prints()
{
    if ${ISSUER} && ! ${VALIDATION}
    then
        if ${NAGIOS}
        then
            ${PRINTF} "%-35s %-17s %-8s %-11s %-4s %-30s\n" "$1:$2" "$6" "$3" "$4" \|days="$5"
        else
            ${PRINTF} "%-35s %-17s %-8s %-11s %-4s %-30s\n" "$1:$2" "$6" "$3" "$4" "$5"
        fi
    elif ${ISSUER} && ${VALIDATION}
    then
        ${PRINTF} "%-35s %-35s %-32s %-17s\n" "$1:$2" "$7" "$8" "$6"

    elif ! ${VALIDATION}
    then
        if ${NAGIOS}
        then
            ${PRINTF} "%-47s %-12s %-12s %-4s %-30s\n" "$1:$2" "$3" "$4" \|days="$5"
        else
            ${PRINTF} "%-47s %-12s %-12s %-4s %-30s\n" "$1:$2" "$3" "$4" "$5"
        fi
    else
        ${PRINTF} "%-35s %-35s %-32s\n" "$1:$2" "$7" "$8"
    fi
}


####################################################
# Purpose: Print a heading with the relevant columns
# Arguments:
#   None
####################################################
print_heading()
{
    if ! ${NOHEADER}
    then
       if ${ISSUER} && ! ${NAGIOS} && ! ${VALIDATION}
       then
           ${PRINTF} "\n%-35s %-17s %-8s %-11s %-4s\n" "Host" "Issuer" "Status" "Expires" "Days" >> ${STDOUT_TMP}
           echo "----------------------------------- ----------------- -------- ----------- ----" >> ${STDOUT_TMP}

       elif ${ISSUER} && ! ${NAGIOS} && ${VALIDATION}
       then
           ${PRINTF} "\n%-35s %-35s %-32s %-17s\n" "Host" "Common Name" "Serial #" "Issuer" >> ${STDOUT_TMP}
           echo "----------------------------------- ----------------------------------- -------------------------------- -----------------" >> ${STDOUT_TMP}

       elif ! ${NAGIOS} && ! ${VALIDATION}
       then
           ${PRINTF} "\n%-47s %-12s %-12s %-4s\n" "Host" "Status" "Expires" "Days" >> ${STDOUT_TMP}
           echo "----------------------------------------------- ------------ ------------ ----" >> ${STDOUT_TMP}

       elif ! ${NAGIOS} && ${VALIDATION}
       then
           ${PRINTF} "\n%-35s %-35s %-32s\n" "Host" "Common Name" "Serial #" >> ${STDOUT_TMP}
           echo "----------------------------------- ----------------------------------- --------------------------------" >> ${STDOUT_TMP}
       fi
       ${PRINTF} "Dear admin of %s,\n" "${HOSTNAME}" >> ${MAILOUT_TMP}
    fi
}


##########################################
# Purpose: Describe how the script works
# Arguments:
#   None
##########################################
usage()
{
    >&2 echo "$(basename "$0") checks expiration of gpg keys and X.509 certificates and sends emails if keys are about to expire."
    >&2 echo ""
    >&2 echo "Usage: $0 [ -e email address ] [ -x days ] [-q] [-a] [-b] [-h] [-i] [-n] [-v] { [ -s common_name:port] } || { [ -f cert_file ] } || { [ -c certificate file ] } || { [ -g email address ] }"
    >&2 echo ""
    >&2 echo "  -a                    Send a warning message through E-mail"
    >&2 echo "  -b                    Will not print header"
    >&2 echo "  -c cert file          Print the expiration date for the PEM or PKCS12 formatted certificate in cert file"
    >&2 echo "  -e E-mail address     E-mail address to send expiration notices"
    >&2 echo "  -f cert file          File with a list of FQDNs and ports"
    >&2 echo "  -g E-mail address     E-mail address to check expiry of gpg-key from"
    >&2 echo "  -G executbl:E-mail    Use 'executbl' instead of 'gpg' for checking expiry of E-mail's key. Must accept --list-keys and --list-secret-keys as gpg does."
    >&2 echo "  -h                    Print this screen"
    >&2 echo "  -i                    Print the issuer of the certificate"
    >&2 echo "  -k password           PKCS12 file password"
    >&2 echo "  -n                    Run as a Nagios plugin"
    >&2 echo "  -N directory          Check nginx certificates in directory's config files."
    >&2 echo "  -q                    Don't print anything on the console"
    >&2 echo "  -r url                Like -c, but download cert from url."
    >&2 echo "  -s commmon_name:port  Server and Port to connect to (interactive mode)"
    >&2 echo "  -t type               Specify the certificate type"
    >&2 echo "  -v                    Specify a specific protocol version to use (tls, ssl2, ssl3)"
    >&2 echo "  -V                    Only print validation data"
    >&2 echo "  -x days               Certificate expiration interval (eg. if cert_date < days)"
    >&2 echo "  -Z                    Print version"
    >&2 echo ""
}


##########################################################################
# Purpose: Connect to a server ($1) and port ($2) to see if a certificate
#          has expired
# Arguments:
#   $1 -> Server name
#   $2 -> TCP port to connect to
##########################################################################
check_server_status() {

    if [ "_${2}" = "_smtp" -o "_${2}" = "_25" ]
    then
        TLSFLAG="-starttls smtp"

    elif [ "_${2}" = "_ftp" -o "_${2}" = "_21" ]
    then
        TLSFLAG="-starttls ftp"

    elif [ "_${2}" = "_pop3" -o "_${2}" = "_110" ]
    then
        TLSFLAG="-starttls pop3"

    elif [ "_${2}" = "_imap" -o "_${2}" = "_143" ]
    then
        TLSFLAG="-starttls imap"

    elif [ "_${2}" = "_submission" -o "_${2}" = "_587" ]
    then
        TLSFLAG="-starttls smtp -port ${2}"
    else
        TLSFLAG=""
    fi

    if [ "${VERSION}" != "" ]
    then
        VER="-${VERSION}"
    fi

    if ${TLSSERVERNAME}
    then
         TLSFLAG="${TLSFLAG} -servername $1"
    fi

    echo "" | ${OPENSSL} s_client ${VER} -connect ${1}:${2} ${TLSFLAG} 2> ${ERROR_TMP} 1> ${CERT_TMP}

    if ${GREP} -iq  "Connection refused" ${ERROR_TMP}
    then
        prints ${1} ${2} "Connection refused" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "Network is unreachable" ${ERROR_TMP}
    then
        prints ${1} ${2} "Network is unreachable" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "No route to host" ${ERROR_TMP}
    then
        prints ${1} ${2} "No route to host" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "gethostbyname failure" ${ERROR_TMP}
    then
        prints ${1} ${2} "Cannot resolve domain" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "Operation timed out" ${ERROR_TMP}
    then
        prints ${1} ${2} "Operation timed out" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "ssl handshake failure" ${ERROR_TMP}
    then
        prints ${1} ${2} "SSL handshake failed" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    elif ${GREP} -iq "connect: Connection timed out" ${ERROR_TMP}
    then
        prints ${1} ${2} "Connection timed out" "Unknown" | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 3

    else
        check_file_status ${CERT_TMP} $1 $2 $3
    fi
}

#####################################################
### Check the expiration status of a certificate file
### on a remote location
### Accepts three parameters:
###  $1 -> certificate file to process
###  $2 -> Server name
###  $3 -> Port number of certificate
#####################################################
check_remote_file_status() {

    CERTURL=${1}
    HOST=${2}
    PORT=${3}
    FWARNDAYS=${4:-${WARNDAYS}}

    if ! curl -so "${CERT_TMP}" "${CERTURL}"
    then
        echo "ERROR: Could not retrieve the certificate at '${CERTURL}'." | ${TEE} -a ${MAILOUT_TMP} >&2
        set_returncode 1
        return
    fi

    check_file_status "${CERT_TMP}" "${HOST}" "${PORT}" "${FWARNDAYS}"

}

#####################################################
### Check the expiration status of nginx certificates
### Accepts one parameter:
###  $1 -> directory to nginx configurations
#####################################################

check_nginx_configuration() {
    CONFIGDIR=${1}
    FWARNDAYS=${2:-${WARNDAYS}}

    while read -r line; do
        {
            read -r _ HOST _
            read -r _ FILE
        } < <(
            printf '%s\n' "${line}" \
            | "${SED}" '
                s/[;{}]/\0\n/g
            ' \
            | "${SED}" -n '
                /^\(server_name\|ssl_certificate\)\s/p
            ' \
            | "${SORT}" \
            | "${SED}" '
                s/;$//
            '
        )
        check_file_status "${FILE}" 'NGINX' "${HOST// /, }" "${WARNDAYS}"
    done < <(
        find "${CONFIGDIR}" \
            \( -type f -o -type l \) \
            -exec "${SED}" '
                s/#.*$//
                s/^\s*//
                s/\s*$//
            ' {} \; \
        | "${TR}" -d '\n' \
        | "${SED}" '
            s/}server\s*{/\n\0/g
        ' \
        | "${GREP}" -wF 'ssl_certificate'
    )
}

#####################################################
### Check the expiration status of a certificate file
### Accepts three parameters:
###  $1 -> certificate file to process
###  $2 -> Server name
###  $3 -> Port number of certificate
#####################################################
check_file_status() {

    CERTFILE=${1}
    HOST=${2}
    PORT=${3}
    FWARNDAYS=${4:-${WARNDAYS}}

    ### Check to make sure the certificate file exists
    if [ ! -r ${CERTFILE} ] || [ ! -s ${CERTFILE} ]
    then
        echo "ERROR: The file named ${CERTFILE} is unreadable or doesn't exist" | ${TEE} -a ${MAILOUT_TMP} >&2
        echo "ERROR: Please check to make sure the certificate for ${HOST}:${PORT} is valid" | ${TEE} -a ${MAILOUT_TMP} >&2
        set_retcode 1
        return
    fi

    ### Grab the expiration date from the X.509 certificate
    if [ "${PKCSDBPASSWD}" != "" ]
    then
        # Extract the certificate from the PKCS#12 database, and
        # send the informational message to /dev/null
        ${OPENSSL} pkcs12 -nokeys -in ${CERTFILE} \
                   -out ${CERT_TMP} -clcerts -password pass:${PKCSDBPASSWD} 2> /dev/null

        # Extract the expiration date from the certificate
        CERTDATE=$(${OPENSSL} x509 -in ${CERT_TMP} -enddate -noout | \
                 ${SED} 's/notAfter\=//')
        CERTDATE=$(${DATE} +%s -d "${CERTDATE}")

        # Extract the issuer from the certificate
        CERTISSUER=$(${OPENSSL} x509 -in ${CERT_TMP} -issuer -noout | \
                    ${AWK} 'BEGIN {RS="/" } $0 ~ /^O=/ \
                                  { print substr($0,3,17)}')

        ### Grab the common name (CN) from the X.509 certificate
        COMMONNAME=$(${OPENSSL} x509 -in ${CERT_TMP} -subject -noout | \
                   ${SED} -e 's/.*CN=//' | \
				   ${SED} -e 's/\/.*//')

	### Grab the serial number from the X.509 certificate
        SERIAL=$(${OPENSSL} x509 -in ${CERT_TMP} -serial -noout | \
                   ${SED} -e 's/serial=//')
    else
        # Extract the expiration date from the ceriticate
        CERTDATE=$(while ${OPENSSL} x509 -enddate -noout -inform ${CERTTYPE} 2>/dev/null; do :; done <${CERTFILE} | \
                 ${SED} 's/notAfter\=//' | \
                 xargs -rI __ ${DATE} +%s -d "__")

        # Extract the issuer from the certificate
        CERTISSUER=$(${OPENSSL} x509 -in ${CERTFILE} -issuer -noout -inform ${CERTTYPE} | \
                   ${AWK} 'BEGIN {RS="/" } $0 ~ /^O=/ { print substr($0,3,17)}')

        ### Grab the common name (CN) from the X.509 certificate
        COMMONNAME=$(${OPENSSL} x509 -in ${CERTFILE} -subject -noout -inform ${CERTTYPE} | \
                   ${SED} -e 's/.*CN=//' | \
				   ${SED} -e 's/\/.*//')
	### Grab the serial number from the X.509 certificate
        SERIAL=$(${OPENSSL} x509 -in ${CERTFILE} -serial -noout -inform ${CERTTYPE} | \
                   ${SED} -e 's/serial=//')
    fi

    # Convert the date to seconds, and get the diff between NOW and the expiration date
    CERTDIFF=$[${CERTDATE} - $(${DATE} +%s)]
    CERTDATE=$(format_date "${CERTDATE}")
    if [ ${CERTDIFF} -lt 0 ]
    then
        CERTDIFF=$[$[${CERTDIFF}+1]/3600/24-1]
    else
        CERTDIFF=$[${CERTDIFF}/3600/24]
    fi

    if [ ${CERTDIFF} -lt 0 ]
    then
        echo "The SSL certificate for ${HOST} \"(CN: ${COMMONNAME})\" has expired!" >> ${MAILOUT_TMP}
        prints ${HOST} ${PORT} "Expired" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" >> ${STDOUT_TMP}
        set_retcode 2

    elif [ ${CERTDIFF} -lt ${FWARNDAYS} ]
    then
        echo "The SSL certificate for ${HOST} \"(CN: ${COMMONNAME})\" will expire on ${CERTDATE}" >> ${MAILOUT_TMP}
        prints ${HOST} ${PORT} "Expiring" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" >> ${STDOUT_TMP}
        set_retcode 1

    else
        prints ${HOST} ${PORT} "Valid" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" >> ${STDOUT_TMP}
    fi
}

#####################################################
### Check the expiration status of a gpg-key
### Accepts one parameters:
###  $1 -> gpg command to use
###  $2 -> E-mail address to check
#####################################################
check_gpg_key_status() {

    GPG_COMMAND="${1}"
    GPG_ADDRESS="${2}"
    FWARNDAYS=${3:-${WARNDAYS}}

    ### Check to make sure gpg is available
    if [ ! -f "${GPG_COMMAND}" ]
    then
        >&2 echo "ERROR: The gnupg binary does not exist in ${GPG_COMMAND}."
        >&2 echo "FIX: Please modify the \${GPG} variable in the program header, provide alternative executable via -G or ommit testing of gpg-keys."
        exit 1
    fi

    KEYS=$(${GPG_COMMAND} --list-secret-keys --with-colons "${GPG_ADDRESS}" 2>/dev/null)
    [ -z "${KEYS}" ] && KEYS=$(${GPG_COMMAND} --list-keys --with-colons "${GPG_ADDRESS}" 2>/dev/null)

    KEY_DATE=$(
        echo "${KEYS}" \
        | ${AWK} -F: '$1 == "fpr" {print $10}' \
        | ${SORT} -u \
        | while read -r KEY; do
            ${GPG_COMMAND} --list-keys --with-colons "${KEY}" \
            | awk -F: '$1 == "sub" || $1 == "pub" {print $7}' \
            | ${SORT} -r \
            | tail -n1
        done \
        | ${SORT} \
        | tail -n1
    )
    if [ -z "${KEY_DATE}" ]
    then
        echo "No valid gpg-key found for ${GPG_ADDRESS}." | ${TEE} -a ${MAILOUT_TMP} >> ${STDOUT_TMP}
        set_retcode 2
        return
    fi

    KEY_DIFF=$[${KEY_DATE} - $(${DATE} +%s)]
    KEY_DATE=$(format_date "${KEY_DATE}")
    if [ ${KEY_DIFF} -lt 0 ]
    then
        KEY_DIFF=$[$[${KEY_DIFF}+1]/3600/24-1]
    else
        KEY_DIFF=$[${KEY_DIFF}/3600/24]
    fi

    if [ ${KEY_DIFF} -lt 0 ]
    then
        echo "The GPG key for ${GPG_ADDRESS} has expired!" >> ${MAILOUT_TMP}
        prints "GPG" " ${GPG_ADDRESS}" "Expired" "${KEY_DATE}" "${KEY_DIFF}" "" "" "" >> ${STDOUT_TMP}
        set_retcode 2

    elif [ ${KEY_DIFF} -lt ${FWARNDAYS} ]
    then
        echo "The GPG key for ${GPG_ADDRESS} will expire on ${KEY_DATE}" >> ${MAILOUT_TMP}
        prints "GPG" " ${GPG_ADDRESS}" "Expiring" "${KEY_DATE}" "${KEY_DIFF}" "" "" "" >> ${STDOUT_TMP}
        set_retcode 1

    else
        prints "GPG" " ${GPG_ADDRESS}" "Valid" "${KEY_DATE}" "${KEY_DIFF}" "" "" "" >> ${STDOUT_TMP}
    fi
}

#################################
### Start of main program
#################################
while getopts abc:e:f:g:G:hik:nN:qr:s:t:v:Vx:Z option
do
    case "${option}"
    in
        a)
            ALARM=true
            ;;
        b)
            NOHEADER=true
            ;;
        c)
            CERTFILES[${#CERTFILES[@]}]=${OPTARG}
            ;;
        e)
            ADMIN=${OPTARG}
            ;;
        f)
            SERVERFILES[${#SERVERFILES[@]}]=${OPTARG}
            ;;
        g)
            CHECKADDRESSES[${#CHECKADDRESSES[@]}]=${OPTARG}
            CHECKADDRESSBINARIES[${#CHECKADDRESSBINARIES[@]}]=${GPG}
            ;;
        G)
            CHECKADDRESSES[${#CHECKADDRESSES[@]}]=${OPTARG#*:}
            CHECKADDRESSBINARIES[${#CHECKADDRESSBINARIES[@]}]=$(which ${OPTARG%%:*})
            ;;
        i)
            ISSUER=true
            ;;
        k)
            PKCSDBPASSWD=${OPTARG}
            ;;
        n)
            NAGIOS=true
            ;;
        N)
            NGINXDIRS[${#NGINXDIRS[@]}]=${OPTARG}
            ;;
        q)
            QUIET=true
            ;;
        r)
            REMOTECERTFILES[${#REMOTECERTFILES[@]}]=${OPTARG}
            ;;
        s)
            HOSTS[${#HOSTS[@]}]=${OPTARG%:*}
            PORTS[${#PORTS[@]}]=${OPTARG#*:}
            ;;
        t)
            CERTTYPE=${OPTARG}
            ;;
        v)
            VERSION=${OPTARG}
            ;;
        V)
            VALIDATION=true
            ;;
        x)
            WARNDAYS=${OPTARG}
            ;;
        Z)
            echo '4.5.1'
            exit 0
            ;;
        *)
            usage
            exit 1
            ;;
    esac
done

if [ ${OPTIND} -le $# ]
then
    >&2 echo "ERROR: Too many arguments."
    exit 1
fi

### Check to make sure a openssl utility is available
if [ ! -f ${OPENSSL} ]
then
    >&2 echo "ERROR: The openssl binary does not exist in ${OPENSSL}."
    >&2 echo "FIX: Please modify the \${OPENSSL} variable in the program header."
    exit 1
fi

### Check to make sure a date utility is available
if [ ! -f ${DATE} ]
then
    >&2 echo "ERROR: The date binary does not exist in ${DATE} ."
    >&2 echo "FIX: Please modify the \${DATE} variable in the program header."
    exit 1
fi

### Check to make sure a grep utility is available
if [ ! -f ${GREP} ]
then
    >&2 echo "ERROR: The grep binary does not exist in ${GREP} ."
    >&2 echo "FIX: Please modify the \${GREP} variable in the program header."
    exit 1
fi

### Check to make sure the mktemp and printf utilities are available
if [ ! -f ${MKTEMP} ] || [ ! -f ${PRINTF} ]
then
    >&2 echo "ERROR: Unable to locate the mktemp or printf binary."
    >&2 echo "FIX: Please modify the \${MKTEMP} and \${PRINTF} variables in the program header."
    exit 1
fi

### Check to make sure the sed and awk binaries are available
if [ ! -f ${SED} ] || [ ! -f ${AWK} ] || [ ! -f ${TEE} ] || [ ! -f ${SORT} ] || [ ! -f ${TAIL} ] || [ ! -f "${TR}" ]
then
    >&2 echo "ERROR: Unable to locate the sed, awk, tee, sort, tail or tr binary."
    >&2 echo "FIX: Please modify the \${SED}, \${AWK}, \${TEE}, \${SORT}, \${TAIL}, \${TR} variables in the program header."
    exit 1
fi

### Check to make sure a mail client is available if automated notifications are requested
if ${ALARM} && [ ! -f ${MAIL} ]
then
    >&2 echo "ERROR: You enabled automated alerts, but the mail binary could not be found."
    >&2 echo "FIX: Please modify the \${MAIL} variable in the program header."
    exit 1
fi

# Send along the servername when TLS is used
TLSSERVERNAME=${OPENSSL} s_client -h 2>&1 | grep -q -- '-servername'

# Place to stash temporary files
CERT_TMP=$(${MKTEMP}  /var/tmp/cert.XXXXXX)
ERROR_TMP=$(${MKTEMP} /var/tmp/error.XXXXXX)
STDOUT_TMP=$(${MKTEMP} /var/tmp/stdout.XXXXXX)
MAILOUT_TMP=$(${MKTEMP} /var/tmp/mailout.XXXXXX)

### Touch the files prior to using them
if [ ! -z "${CERT_TMP}" ] && [ ! -z "${ERROR_TMP}" ] && [ ! -z "${STDOUT_TMP}" ] && [ ! -z "${MAILOUT_TMP}" ]
then
    touch ${CERT_TMP} ${ERROR_TMP} ${STDOUT_TMP} ${MAILOUT_TMP}
else
    >&2 echo "ERROR: Problem creating temporary files"
    >&2 echo "FIX: Check that mktemp works on your system"
    exit 1
fi

if [ $[${#HOSTS[@]} + ${#SERVERFILES[@]} + ${#CERTFILES[@]} + ${#REMOTECERTFILES[@]} + ${#CHECKADDRESSES[@]} + ${#NGINXDIRS[@]}] -eq 0 ]
then
    >&2 echo "ERROR: Nothing to check."
    usage
    exit 1
fi

print_heading

for (( i=0; i<${#HOSTS[@]}; i++ ))
do
    check_server_status "${HOSTS[${i}]}" "${PORTS[${i}]}"
done

for (( i=0; i<${#NGINXDIRS[@]}; i++ ))
do
    check_nginx_configuration "${NGINXDIRS[@]}"
#            check_file_status "${HOST}" "FILE" "${HOST}" "${FWARNDAYS}"
done

for (( i=0; i<${#SERVERFILES[@]}; i++ ))
do
    while read FWARNDAYS PORT HOST
    do
        if [ "${PORT}" = "FILE" ]
        then
            check_file_status "${HOST}" "FILE" "${HOST}" "${FWARNDAYS}"
        elif [ "${PORT}" = "REMOTEFILE" ]
        then
            check_remote_file_status "${HOST}" "REMOTEFILE" "${HOST}" "${FWARNDAYS}"
        elif [ "${PORT}" = "NGINX" ]
        then
            check_nginx_configuration "${HOST}" "${FWARNDAYS}"
        elif [ "${PORT}" = "GPG" ]
        then
            check_gpg_key_status "${GPG}" "${HOST}" "${FWARNDAYS}"
        elif [[ "${PORT}" = "GPG:"* ]]
        then
            check_gpg_key_status "$(which ${PORT#*:})" "${HOST}" "${FWARNDAYS}"
        else
            check_server_status "${HOST}" "${PORT}" "${FWARNDAYS}"
        fi
    done < <(
        "${SED}" '
          /^#\|^$/d
          s/^\([0-9]\+\) \+\(.*\S\) \+\(\S\+\)$/\1 \3 \2/
          t
          s/^\(.*\S\) \+\(\S\+\)$/'"${WARNDAYS}"' \2 \1/
        ' ${SERVERFILES[${i}]}
    )
done

for (( i=0; i<${#CERTFILES[@]}; i++ ))
do
    check_file_status "${CERTFILES[${i}]}" "FILE"  "${CERTFILES[${i}]}"
done

for (( i=0; i<${#REMOTECERTFILES[@]}; i++ ))
do
    check_remote_file_status "${REMOTECERTFILES[${i}]}" "REMOTEFILE"  "${REMOTECERTFILES[${i}]}"
done

for (( i=0; i<${#CHECKADDRESSES[@]}; i++ ))
do
    check_gpg_key_status "${CHECKADDRESSBINARIES[${i}]}" "${CHECKADDRESSES[${i}]}"
done

if ! ${QUIET}
then
    cat ${STDOUT_TMP}
fi

if ${ALARM} && [ ${RETCODE} -gt 0 ]
then
    (
        echo "To: ${ADMIN}"
        echo "From: $(whoami)@$(hostname)"
        echo "Subject: $(basename $0) at $(${DATE})"
        echo ""
        cat ${MAILOUT_TMP}
    ) | ${MAIL} -t
fi

### Remove the temporary files
if ${DEBUG}
then
    echo "DEBUG: Certificate temporary file:"
    cat ${CERT_TMP}
    echo "DEBUG: Runtime information file:"
    cat ${ERROR_TMP}
fi

rm -f ${CERT_TMP} ${ERROR_TMP} ${STDOUT_TMP} ${MAILOUT_TMP}

### Exit with a success indicator
if ${NAGIOS}
then
    exit ${RETCODE}
else
    exit 0
fi
