#!/bin/sh
#
# check_ssl_cert
#
# Checks an X.509 certificate:
# - checks if the server is running and delivers a valid certificate
# - checks if the CA matches a given pattern
# - checks the validity
#
# See  the INSTALL file for installation instructions
#
# Copyright (c) 2007-2012 ETH Zurich.
# Copyright (c) 2007-2016 Matteo Corti <matteo@corti.li>
#
# This module is free software; you can redistribute it and/or modify it
# under the terms of GNU general public license (gpl) version 3.
# See the LICENSE file for details.

################################################################################
# Constants

VERSION=1.37.0
SHORTNAME="SSL_CERT"

VALID_ATTRIBUTES=",startdate,enddate,subject,issuer,serial,modulus,serial,hash,email,ocsp_uri,fingerprint,"

################################################################################
# Functions

################################################################################
# Prints usage information
# Params
#   $1 error message (optional)
usage() {

    if [ -n "$1" ] ; then
        echo "Error: $1" 1>&2
    fi

    #### The following line is 80 characters long (helps to fit the help text in a standard terminal)
    ######--------------------------------------------------------------------------------

    echo
    echo "Usage: check_ssl_cert -H host [OPTIONS]"
    echo
    echo "Arguments:"
    echo "   -H,--host host             server"
    echo
    echo "Options:"
    echo "   -A,--noauth                ignore authority warnings (expiration only)"
    echo "      --altnames              matches the pattern specified in -n with alternate"
    echo "                              names too"
    echo "   -C,--clientcert path       use client certificate to authenticate"
    echo "      --clientpass phrase     set passphrase for client certificate."
    echo "   -c,--critical days         minimum number of days a certificate has to be valid"
    echo "                              to issue a critical status"
    echo "   -d,--debug                 produces debugging output"
    echo "   -e,--email address         pattern to match the email address contained in the"
    echo "                              certificate"
    echo "       --ecdsa                cipher selection: force ECDSA authentication"
    echo "   -f,--file file             local file path (works with -H localhost only)"
    echo "      --file-bin path         path of the file binary to be used"
    echo "   -h,--help,-?               this help message"
    echo "      --ignore-exp            ignore expiration date"
    echo "      --ignore-sig-alg        do not check if the certificate was signed with SHA1"
    echo "                              or MD5"
    echo "      --ignore-ocsp           do not check revocation with OCSP"
    echo "   -i,--issuer issuer         pattern to match the issuer of the certificate"
    echo "   -L,--check-ssl-labs grade  SSL Labs assestment"
    echo "                              (please check https://www.ssllabs.com/about/terms.html)"
    echo "      --ignore-ssl-labs-cache Forces a new check by SSL Labs (see -L)"
    echo "      --long-output list      append the specified comma separated (no spaces) list"
    echo "                              of attributes to the plugin output on additional lines"
    echo "                              Valid attributes are:"
    echo "                                enddate, startdate, subject, issuer, modulus,"
    echo "                                serial, hash, email, ocsp_uri and fingerprint."
    echo "                              'all' will include all the available attributes."
    echo "   -n,--cn name               pattern to match the CN of the certificate (can be"
    echo "                              specified multiple times)"
    echo "      --no_ssl2               disable SSL version 2"
    echo "      --no_ssl3               disable SSL version 3"
    echo "      --no_tls1               disable TLS version 1"
    echo "      --no_tls1_1             disable TLS version 1.1"
    echo "      --no_tls1_2             disable TLS version 1.2"
    echo "   -N,--host-cn               match CN with the host name"
    echo "   -o,--org org               pattern to match the organization of the certificate"
    echo "      --openssl path          path of the openssl binary to be used"
    echo "   -p,--port port             TCP port"
    echo "   -P,--protocol protocol     use the specific protocol {http|smtp|pop3|imap|ftp|xmpp|irc}"
    echo "                              http:               default"
    echo "                              smtp,pop3,imap,ftp: switch to TLS"
    echo "   -s,--selfsigned            allows self-signed certificates"
    echo "      --serial serialnum      pattern to match the serial number"
    echo "      --ssl2                  force SSL version 2"
    echo "      --ssl3                  force SSL version 3"
    echo "   -r,--rootcert path         root certificate or directory to be used for"
    echo "                              certificate validation"
    echo "       --rsa                  cipher selection: force RSA authentication"
    echo "   -t,--timeout               seconds timeout after the specified time"
    echo "                              (defaults to 15 seconds)"
    echo "      --temp dir              directory where to store the temporary files"
    echo "      --tls1                  force TLS version 1"
    echo "      --tls1_1                force TLS version 1.1"
    echo "      --tls1_2                force TLS version 1.2"
    echo "   -v,--verbose               verbose output"
    echo "   -V,--version               version"
    echo "   -w,--warning days          minimum number of days a certificate has to be valid"
    echo "                              to issue a warning status"
    echo
    echo "Deprecated options:"
    echo "   -d,--days days             minimum number of days a certificate has to be valid"
    echo "                              (see --critical and --warning)"
    echo "      --ocsp                  check revocation via OCSP"
    echo "   -S,--ssl version           force SSL version (2,3)"
    echo "                              (see: --ss2 or --ssl3)"
    echo
    echo "Report bugs to https://github.com/matteocorti/check_ssl_cert/issues"
    echo

    exit 3

}

################################################################################
# Exits with a critical message
# Params
#   $1 error message
critical() {
    if [ -n "${CN}" ] ; then
        tmp=" ${CN}"
    fi
    printf '%s CRITICAL%s: %s%s%s\n' "${SHORTNAME}" "${tmp}" "$1" "${PERFORMANCE_DATA}" "${LONG_OUTPUT}"
    exit 2
}

################################################################################
# Exits with a warning message
# Param
#   $1 warning message
warning() {
    if [ -n "${CN}" ] ; then
        tmp=" ${CN}"
    fi
    printf '%s WARN%s: %s%s%s\n' "${SHORTNAME}" "${tmp}" "$1" "${PERFORMANCE_DATA}" "${LONG_OUTPUT}"
    exit 1
}

################################################################################
# Exits with an 'unkown' status
# Param
#   $1 message
unknown() {
    if [ -n "${CN}" ] ; then
        tmp=" ${CN}"
    fi
    printf '%s UNKNOWN%s: %s\n' "${SHORTNAME}" "${tmp}" "$1"
    exit 3
}

################################################################################
# Executes command with a timeout
# Params:
#   $1 timeout in seconds
#   $2 command
# Returns 1 if timed out 0 otherwise
exec_with_timeout() {

    time=$1

    # start the command in a subshell to avoid problem with pipes
    # (spawn accepts one command)
    command="/bin/sh -c \"$2\""

    if [ -n "${DEBUG}" ] ; then
        echo "[DBG] executing with timeout (${time}s): $2"
    fi

    if [ -n "${TIMEOUT_BIN}" ] ; then

        if [ -n "${DEBUG}" ] ; then
            echo "[DBG]   ${TIMEOUT_BIN} $time $command"
        fi

        eval "${TIMEOUT_BIN} $time $command" > /dev/null 2>&1

        if [ $? = 137 ] ; then
            critical "Timeout after ${time} seconds"
        fi

    elif [ -n "${EXPECT}" ] ; then

        expect -c "set echo \"-noecho\"; set timeout $time; spawn -noecho $command; expect timeout { exit 1 } eof { exit 0 }"

        if [ $? = 1 ] ; then
            critical "Timeout after ${time} seconds"
        fi

    else

        eval "${command}"

    fi

}

################################################################################
# Checks if a given program is available and executable
# Params
#   $1 program name
# Returns 1 if the program exists and is executable
check_required_prog() {

    PROG=$(which "$1" 2> /dev/null)

    if [ -z "$PROG" ] ; then
        critical "cannot find $1"
    fi

    if [ ! -x "$PROG" ] ; then
        critical "$PROG is not executable"
    fi

}

################################################################################
# Converts SSL Labs grades to a numeric value
#   (see https://www.ssllabs.com/downloads/SSL_Server_Rating_Guide.pdf)
# Params
#   $1 program name
# Sets NUMERIC_SSL_LAB_GRADE
convert_ssl_lab_grade() {

    GRADE="$1"

    unset NUMERIC_SSL_LAB_GRADE

    case "${GRADE}" in
        'A+'|'a+')
            # Value not in documentation
            NUMERIC_SSL_LAB_GRADE=85
            shift
            ;;
        A|a)
            NUMERIC_SSL_LAB_GRADE=80
            shift
            ;;
        'A-'|'a-')
            # Value not in documentation
            NUMERIC_SSL_LAB_GRADE=75
            shift
            ;;
        B|b)
            NUMERIC_SSL_LAB_GRADE=65
            shift
            ;;
        C|c)
            NUMERIC_SSL_LAB_GRADE=50
            shift
            ;;
        D|d)
            NUMERIC_SSL_LAB_GRADE=35
            shift
            ;;
        E|e)
            NUMERIC_SSL_LAB_GRADE=20
            shift
            ;;
        F|f)
            NUMERIC_SSL_LAB_GRADE=0
            shift
            ;;
        T|t)
            # No trust: value not in documentation
            NUMERIC_SSL_LAB_GRADE=0
            shift
            ;;
        M|m)
            # Certificate name mismatch: value not in documentation
            NUMERIC_SSL_LAB_GRADE=0
            shift
            ;;
        *)
            unknown "Connot convert SSL Lab grade ${GRADE}"
            ;;
    esac

}

################################################################################
# Tries to fetch the certificate

fetch_certificate() {

    RET=0
    
    # Check if a protocol was specified (if not HTTP switch to TLS)
    if [ -n "${PROTOCOL}" ] && [ "${PROTOCOL}" != "http" ] && [ "${PROTOCOL}" != "https" ] ; then

        case "${PROTOCOL}" in
            smtp)
                exec_with_timeout "$TIMEOUT" "echo -e 'QUIT\r' | $OPENSSL s_client ${CLIENT} ${CLIENTPASS} -starttls ${PROTOCOL} -connect $HOST:$PORT ${SERVERNAME} -verify 6 ${ROOT_CA} ${SSL_VERSION} ${SSL_VERSION_DISABLED} ${SSL_AU} 2> ${ERROR} 1> ${CERT}"
		RET=$?
                ;;
            irc)
                exec_with_timeout "$TIMEOUT" "echo -e 'QUIT\r' | $OPENSSL s_client ${CLIENT} ${CLIENTPASS} -connect $HOST:$PORT ${SERVERNAME} -verify 6 ${ROOT_CA} ${SSL_VERSION} ${SSL_VERSION_DISABLED} ${SSL_AU} 2> ${ERROR} 1> ${CERT}"
		RET=$?
                ;;
            pop3|imap|ftp|xmpp)
                exec_with_timeout "$TIMEOUT" "echo 'Q' | $OPENSSL s_client ${CLIENT} ${CLIENTPASS} -starttls ${PROTOCOL} -connect $HOST:$PORT ${SERVERNAME} -verify 6 ${ROOT_CA} ${SSL_VERSION} ${SSL_VERSION_DISABLED} ${SSL_AU} 2> ${ERROR} 1> ${CERT}"
		RET=$?
                ;;
            *)
                unknown "Error: unsupported protocol ${PROTOCOL}"
                ;;
        esac

    elif [ -n "${FILE}" ] ; then

        if [ "${HOST}" = "localhost" ] ; then
            exec_with_timeout "$TIMEOUT" "/bin/cat '${FILE}' 2> ${ERROR} 1> ${CERT}"
	    RET=$?
        else
            unknown "Error: option 'file' works with -H localhost only"
        fi

    else

        exec_with_timeout "$TIMEOUT" "echo 'Q' | $OPENSSL s_client ${CLIENT} ${CLIENTPASS} -connect $HOST:$PORT ${SERVERNAME} -verify 6 ${ROOT_CA} ${SSL_VERSION} ${SSL_VERSION_DISABLED} ${SSL_AU} 2> ${ERROR} 1> ${CERT}"
	RET=$?

    fi

    if [ -n "${DEBUG}" ] ; then
        echo "[DBG] storing a copy of the retrieved certificate in ${HOST}.crt"
        cp "${CERT}" "${HOST}.crt"
        echo "[DBG] storing a copy of the OpenSSL errors in ${HOST}.error"
        cp "${ERROR}" "${HOST}.error"
    fi

    if [ "${RET}" -ne 0 ] ; then

        if [ -n "${DEBUG}" ] ; then
            sed 's/^/[DBG] SSL error: /' "${ERROR}"
        fi

        # s_client could verify the server certificate because the server requires a client certificate
        if grep -q '^Acceptable client certificate CA names' "${CERT}" ; then

            if [ -n "${VERBOSE}" ] ; then
                echo "The server requires a client certificate"
            fi

        else

            # Try to clean up the error message
            #     Remove the 'verify and depth' lines
            #     Take the 1st line (seems OK with the use cases I tested)
            ERROR_MESSAGE=$(
                grep -v '^depth' "${ERROR}" \
                    | grep -v '^verify' \
                    | head -n 1
                 )
            critical "SSL error: ${ERROR_MESSAGE}"

        fi

    fi

}

################################################################################
# Adds metric to performance data
# Params
#   $1 performance data in nagios plugin format,
#      see https://nagios-plugins.org/doc/guidelines.html#AEN200
add_performance_data() {
    if [ -z "${PERFORMANCE_DATA}" ]; then
        PERFORMANCE_DATA="|${1}"
    else 
        PERFORMANCE_DATA="${PERFORMANCE_DATA} $1"
    fi
}

################################################################################
# Main
################################################################################
main() {

    # Default values
    DEBUG=""
    OPENSSL=""
    FILE_BIN=""
    IGNORE_SSL_LABS_CACHE=""
    PORT="443"
    TIMEOUT="15"
    VERBOSE=""
    OCSP="1" # enabled by default

    # Set the default temp dir if not set
    if [ -z "${TMPDIR}" ] ; then
        TMPDIR="/tmp"
    fi

    ################################################################################
    # Process command line options
    #
    # We do no use getopts since it is unable to process long options

    while true; do

        case "$1" in
            ########################################
            # Options without arguments
            -A|--noauth)
                NOAUTH=1
                shift
                ;;
            --altnames)
                ALTNAMES=1
                shift
                ;;
            -d|--debug)
                DEBUG=1
                VERBOSE=1
                shift
                ;;
            -h|--help|-\?)
                usage
                exit 0
                ;;
            --ignore-exp)
                NOEXP=1
                shift
                ;;
            --ignore-sig-alg)
                NOSIGALG=1
                shift
                ;;
            --ignore-ssl-labs-cache)
                IGNORE_SSL_LABS_CACHE="&startNew=on"
                shift
                ;;
            --no_ssl2)
                SSL_VERSION_DISABLED="${SSL_VERSION_DISABLED} -no_ssl2"
                shift
                ;;
            --no_ssl3)
                SSL_VERSION_DISABLED="${SSL_VERSION_DISABLED} -no_ssl3"
                shift
                ;;
            --no_tls1)
                SSL_VERSION_DISABLED="${SSL_VERSION_DISABLED} -no_tls1"
                shift
                ;;
            --no_tls1_1)
                SSL_VERSION_DISABLED="${SSL_VERSION_DISABLED} -no_tls1_1"
                shift
                ;;
            --no_tls1_2)
                SSL_VERSION_DISABLED="${SSL_VERSION_DISABLED} -no_tls1_2"
                shift
                ;;
            -N|--host-cn)
                COMMON_NAME="__HOST__"
                shift
                ;;
            -s|--selfsigned)
                SELFSIGNED=1
                shift
                ;;
	    --rsa)
		SSL_AU="-cipher aRSA"
		shift
		;;
	    --ecdsa)
		SSL_AU="-cipher aECDSA"
		shift
		;;
            --ssl2)
                SSL_VERSION="-ssl2"
                shift
                ;;
            --ssl3)
                SSL_VERSION="-ssl3"
                shift
                ;;
            --tls1)
                SSL_VERSION="-tls1"
                shift
                ;;
            --tls1_1)
                SSL_VERSION="-tls1_1"
                shift
                ;;
            --tls1_2)
                SSL_VERSION="-tls1_2"
                shift
                ;;
	    --ocsp)
		# deprecated
		shift
		;;
            --ignore-ocsp)
                OCSP=""
                shift
                ;;
            -v|--verbose)
                VERBOSE=1
                shift
                ;;
            -V|--version)
                echo "check_ssl_cert version ${VERSION}"
                exit 3
                ;;
            ########################################
            # Options with arguments
            -c|--critical)
                if [ $# -gt 1 ]; then
                    CRITICAL="$2"
                    shift 2
                else
                   unknown "-c,--critical requires an argument"
                fi
                ;;
            # Deprecated option: used to be as --warning
            -d|--days)
                if [ $# -gt 1 ]; then
                    WARNING="$2"
                    shift 2
                else
                    unknown "-d,--days requires an argument"
                fi
                ;;
            -e|--email)
                if [ $# -gt 1 ]; then
                    ADDR="$2"
                    shift 2
                else
                    unknown "-e,--email requires an argument"
                fi
                ;;
            -f|--file)
                if [ $# -gt 1 ]; then
                    FILE="$2"
                    shift 2
                else
                    unknown "-f,--file requires an argument"
                fi
                ;;
            --file-bin)
                if [ $# -gt 1 ]; then
                    FILE_BIN="$2"
                    shift 2
                else
                    unknown "--file-bin requires an argument"
                fi
                ;;
            -H|--host)
                if [ $# -gt 1 ]; then
                    HOST="$2"
                    shift 2
                else
                    unknown "-H,--host requires an argument"
                fi
                ;;
            -i|--issuer)
                if [ $# -gt 1 ]; then
                    ISSUER="$2"
                    shift 2
                else
                    unknown "-i,--issuer requires an argument"s
                fi
                ;;
            -L|--check-ssl-labs)
                if [ $# -gt 1 ]; then
                    SSL_LAB_ASSESTMENT="$2"
                    shift 2
                else
                    unknown "-L|--check-ssl-labs requires an argument"
                fi
                ;;
            --serial)
                if [ $# -gt 1 ]; then
                    SERIAL_LOCK="$2"
                    shift 2
                else
                    unknown "-i,--issuer requires an argument"
                fi
                ;;
            --long-output)
                if [ $# -gt 1 ]; then
                    LONG_OUTPUT_ATTR="$2"
                    shift 2
               else
                    unknown "--long-output requires an argument"
               fi
               ;;
            -n|--cn)
                if [ $# -gt 1 ]; then
		    if [ -n "${COMMON_NAME}" ]; then
		      COMMON_NAME="${COMMON_NAME} ${2}"
		    else
                      COMMON_NAME="${2}"
		    fi
                    shift 2
                else
                    unknown "-n,--cn requires an argument"
                fi
                ;;
            -o|--org)
                if [ $# -gt 1 ]; then
                  ORGANIZATION="$2"
                  shift 2
                else
                    unknown "-o,--org requires an argument"
                fi
                ;;
            --openssl)
                if [ $# -gt 1 ]; then
                    OPENSSL="$2"
                    shift 2
                else
                    unknown "--openssl requires an argument"
                fi
                ;;
            -p|--port)
                if [ $# -gt 1 ]; then
                    PORT="$2"
                    shift 2
                else
                    unknown "-p,--port requires an argument"
                fi
                ;;
            -P|--protocol)
                if [ $# -gt 1 ]; then
                    PROTOCOL="$2"
                    shift 2
                else
                    unknown "-P,--protocol requires an argument"
                fi
                ;;
            -r|--rootcert)
                if [ $# -gt 1 ]; then
                    ROOT_CA="$2"
                    shift 2
                else
                    unknown "-r,--rootcert requires an argument"
                fi
                ;;
            -C|--clientcert)
                if [ $# -gt 1 ]; then
                    CLIENT_CERT="$2"
                    shift 2
                else
                    unknown "-c,--clientcert requires an argument"
                fi
                ;;
            --clientpass)
                if [ $# -gt 1 ]; then
                    CLIENT_PASS="$2"
                    shift 2
                else
                    unknown "--clientpass requires an argument"
                fi
                ;;
            -S|--ssl)
                if [ $# -gt 1 ]; then

                    if [ "$2" = "2" ] || [ "$2" = "3" ] ; then
                        SSL_VERSION="-ssl${2}"
                        shift 2
                    else
                        unknown "invalid argument for --ssl"
                    fi

                else

                    unknown "--ssl requires an argument"

                fi
                ;;
            -t|--timeout)
                if [ $# -gt 1 ]; then
                    TIMEOUT="$2"
                    shift 2
                else
                    unknown "-t,--timeout requires an argument"
                fi
                ;;
            --temp)
                if [ $# -gt 1 ] ; then
                    # Override TMPDIR
                    TMPDIR="$2"
                    shift 2
                else
                    unknown "--temp requires an argument"
                fi
                ;;
            -w|--warning)
                if [ $# -gt 1 ]; then
                    WARNING="$2"
                    shift 2
                else
                    unknown "-w,--warning requires an argument"
                fi
                ;;
            ########################################
            # Special
            --)
                shift
                break
                ;;
            -*)
                unknown "invalid option: ${1}"
                ;;
            *)
                break
                ;;
        esac

    done

    ################################################################################
    # Set COMMON_NAME to hostname if -N was given as argument
    if [ "$COMMON_NAME" = "__HOST__" ] ; then
        COMMON_NAME="${HOST}"
    fi

    ################################################################################
    # Sanity checks

    ###############
    # Check options
    if [ -z "${HOST}" ] ; then
        usage "No host specified"
    fi

    if [ -n "${ALTNAMES}" ] && [ -z "${COMMON_NAME}" ] ; then
        unknown "--altnames requires a common name to match (--cn or --host-cn)"
    fi

    if [ -n "${ROOT_CA}" ] ; then

        if [ ! -r "${ROOT_CA}" ] ; then
            unknown "Cannot read root certificate ${ROOT_CA}"
        fi

        if [ -d "${ROOT_CA}" ] ; then
            ROOT_CA="-CApath ${ROOT_CA}"
        elif [ -f "${ROOT_CA}" ] ; then
            ROOT_CA="-CAfile ${ROOT_CA}"
        else
            unknown "Root certificate of unknown type $(file "${ROOT_CA}" 2> /dev/null)"
        fi

    fi

    if [ -n "${CLIENT_CERT}" ] ; then

        if [ ! -r "${CLIENT_CERT}" ] ; then
            unknown "Cannot read client certificate ${CLIENT_CERT}"
        fi

    fi

    if [ -n "${CRITICAL}" ] ; then

        if ! echo "${CRITICAL}" | grep -q '[0-9][0-9]*' ; then
            unknown "invalid number of days ${CRITICAL}"
        fi

    fi

    if [ -n "${WARNING}" ] ; then

        if ! echo "${WARNING}" | grep -q '[0-9][0-9]*' ; then
            unknown "invalid number of days ${WARNING}"
        fi

    fi

    if [ -n "${CRITICAL}" ] && [ -n "${WARNING}" ] ; then

        if [ "${WARNING}" -le "${CRITICAL}" ] ; then
            unknown "--warning (${WARNING}) is less than or equal to --critical (${CRITICAL})"
        fi

    fi

    if [ -n "${TMPDIR}" ] ; then

        if [ ! -d "${TMPDIR}" ] ; then
            unknown "${TMPDIR} is not a directory";
        fi

        if [ ! -w "${TMPDIR}" ] ; then
            unknown "${TMPDIR} is not writable";
        fi

    fi

    if [ -n "${OPENSSL}" ] ; then

        if [ ! -x "${OPENSSL}" ] ; then
            unknown "${OPENSSL} ist not an executable"
        fi

        if ! "${OPENSSL}" list-standard-commands | grep -q s_client ; then
            unknown "${OPENSSL} ist not an openssl executable"
        fi

    fi

    if [ -n "${SSL_LAB_ASSESTMENT}" ] ; then
        convert_ssl_lab_grade "${SSL_LAB_ASSESTMENT}"
        SSL_LAB_ASSESTMENT_NUMERIC="${NUMERIC_SSL_LAB_GRADE}"
    fi

    if [ -n "${DEBUG}" ] ; then
        echo "[DBG] ROOT_CA = ${ROOT_CA}"
    fi

    #######################
    # Check needed programs

    # OpenSSL
    if [ -z "${OPENSSL}" ] ; then
        check_required_prog openssl
        OPENSSL=$PROG
    fi

    # file
    if [ -z "${FILE_BIN}" ] ; then
        check_required_prog file
        FILE_BIN=$PROG
    fi

    # Expect (optional)
    EXPECT="$(which expect 2> /dev/null)"
    test -x "${EXPECT}" || EXPECT=""
    if [  -n "${VERBOSE}" ] ; then
        if [ -z "${EXPECT}" ] ; then
            echo "expect not available"
        else
            echo "expect available (${EXPECT})"
        fi
    fi

    # Timeout (optional)
    TIMEOUT_BIN="$(which timeout 2> /dev/null)"
    test -x "${TIMEOUT_BIN}" || TIMEOUT_BIN=""
    if [  -n "${VERBOSE}" ] ; then

        if [ -z "${TIMEOUT_BIN}" ] ; then
            echo "timeout not available"
        else
            echo "timeout available (${TIMEOUT_BIN})"
        fi

    fi

    if [ -z "${TIMEOUT_BIN}" ] && [ -z "${EXPECT}" ] && [ -n "${VERBOSE}" ] ; then
        echo "disabling timeouts"
    fi

    PERL="$(which perl 2> /dev/null)"

    if [ -n "${DEBUG}" ] && [ -n "${PERL}" ] ; then
	echo "[DBG] perl available: ${PERL}"
    fi
    
    DATEBIN="$(which date 2> /dev/null)"
    
    if [ -n "${DEBUG}" ] && [ -n "${DATEBIN}" ] ; then
	echo "[DBG] date available: ${DATEBIN}"
    fi

    DATETYPE=""

    if ! "${DATEBIN}" +%s >/dev/null 2>&1  ;  then
		
        # Perl with Date::Parse (optional)
        test -x "${PERL}" || PERL=""
        if [ -z "${PERL}" ] && [ -n "${VERBOSE}" ] ; then
            echo "Perl not found: disabling date computations"
        fi

        if ! ${PERL} -e "use Date::Parse;" > /dev/null 2>&1 ; then

            if [ -n "${VERBOSE}" ] ; then
                echo "Perl module Date::Parse not installed: disabling date computations"
            fi

	    PERL=""

        else

            if [ -n "${VERBOSE}" ] ; then
                echo "Perl module Date::Parse installed: enabling date computations"
            fi	   

            DATETYPE="PERL"

        fi

    else

	if $DATEBIN --version >/dev/null 2>&1 ; then
            DATETYPE="GNU"
	else
            DATETYPE="BSD"
	fi

	if [ -n "${VERBOSE}" ] ; then
            echo "found ${DATETYPE} date with timestamp support: enabling date computations"
	fi

    fi

    if [ -n "${DEBUG}" ] ; then
        echo "[DBG] check_ssl_version: ${VERSION}"
        echo "[DBG] OpenSSL binary: ${OPENSSL}"
        echo "[DBG] OpenSSL version: $( ${OPENSSL} version )"
        echo "[DBG] System info: $( uname -a )"
    fi

    ################################################################################
    # Check if openssl s_client supports the -servername option
    #
    #   openssl s_client does not have a -help option
    #   => We supply an invalid command line option to get the help
    #      on standard error
    #
    SERVERNAME=
    if ${OPENSSL} s_client not_a_real_option 2>&1 | grep -q -- -servername ; then
	
        if [ -n "${COMMON_NAME}" ] && [ "${COMMON_NAME}" = "$(echo "${COMMON_NAME}" | tr -d ' ')" ] ; then
            SERVERNAME="-servername ${COMMON_NAME}"
        else
            SERVERNAME="-servername ${HOST}"
        fi

	if [ -n "${DEBUG}" ] ; then
            echo "[DBG] '${OPENSSL} s_client' supports '-servername': using ${SERVERNAME}"
	fi

	
    else

        if [ -n "${VERBOSE}" ] ; then
            echo "'${OPENSSL} s_client' does not support '-servername': disabling virtual server support"
        fi

    fi

    ################################################################################
    # Fetch the X.509 certificate

    # Temporary storage for the certificate and the errors
    CERT="$( mktemp -t "${0##*/}XXXXXX" 2> /dev/null )"
    if [ -z "${CERT}" ] || [ ! -w "${CERT}" ] ; then
        unknown 'temporary file creation failure.'
    fi

    ERROR="$( mktemp -t "${0##*/}XXXXXX" 2> /dev/null )"
    if [ -z "${ERROR}" ] || [ ! -w "${ERROR}" ] ; then
        unknown 'temporary file creation failure.'
    fi

    if [ -n "${OCSP}" ] ; then

        ISSUER_CERT="$( mktemp -t "${0##*/}XXXXXX" 2> /dev/null )"
        if [ -z "${ISSUER_CERT}" ] || [ ! -w "${ISSUER_CERT}" ] ; then
            unknown 'temporary file creation failure.'
        fi

    fi

    if [ -n "${VERBOSE}" ] ; then
        echo "downloading certificate to ${TMPDIR}"
    fi

    CLIENT=""
    if [ -n "${CLIENT_CERT}" ] ; then
        CLIENT="-cert ${CLIENT_CERT}"
    fi

    CLIENTPASS=""
    if [ -n "${CLIENT_PASS}" ] ; then
        CLIENTPASS="-pass pass:${CLIENT_PASS}"
    fi

    # Cleanup before program termination
    # Using named signals to be POSIX compliant
    trap 'rm -f $CERT $ERROR $ISSUER_CERT' EXIT HUP INT QUIT TERM

    fetch_certificate

    if grep -q 'sslv3\ alert\ unexpected\ message' "${ERROR}" ; then

        if [ -n "${SERVERNAME}" ] ; then

            # Some OpenSSL versions have problems with the -servername option
            # We try without
            if [ -n "${VERBOSE}" ] ; then
                echo "'${OPENSSL} s_client' returned an error: trying without '-servername'"
            fi

            SERVERNAME=""
            fetch_certificate

        fi

        if grep -q 'sslv3\ alert\ unexpected\ message' "${ERROR}" ; then

            critical "cannot fetch certificate: OpenSSL got an unexpected message"

        fi

    fi

    if ! grep -q "CERTIFICATE" "${CERT}" ; then

        if [ -n "${FILE}" ] ; then
            critical "'${FILE}' is not a valid certificate file"
        else
            # See
            # http://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n
            #
            # - create a branch label via :a
            # - the N command appends a newline and and the next line of the input
            #   file to the pattern space
            # - if we are before the last line, branch to the created label $!ba
            #   ($! means not to do it on the last line (as there should be one final newline))
            # - finally the substitution replaces every newline with a space on
            #   the pattern space
            ERROR_MESSAGE="$(sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/; /g' "${ERROR}")"
            if [ -n "${VERBOSE}" ] ; then
                echo "Error: ${ERROR_MESSAGE}"
            fi
            critical "No certificate returned"
        fi

    fi

    if [ -n "${VERBOSE}" ] ; then
        echo "parsing the certificate file"
    fi

    ################################################################################
    # Parse the X.509 certificate
    DATE="$($OPENSSL x509 -in "${CERT}" -enddate -noout | sed -e "s/^notAfter=//")"

    # we need to remove everything before 'CN = ', to remove an eventual email supplied with / and additional elements (after ', ')
    CN="$($OPENSSL x509 -in "${CERT}" -subject -noout -nameopt utf8,oneline,-esc_msb |
        sed -e "s/^.*[[:space:]]CN[[:space:]]=[[:space:]]//"  -e "s/\/[[:alpha:]][[:alpha:]]*=.*\$//" -e "s/,.*//" )"

    CA_O="$($OPENSSL x509 -in "${CERT}" -issuer -noout | sed -e "s/^.*\/O=//" -e "s/\/[A-Z][A-Z]*=.*\$//")"
    CA_CN="$($OPENSSL x509 -in "${CERT}" -issuer -noout  | sed -e "s/^.*\/CN=//" -e "s/\/[A-Za-z][A-Za-z]*=.*\$//")"

    SERIAL="$($OPENSSL x509 -in "${CERT}" -serial -noout  | sed -e "s/^serial=//")"

    OCSP_URI="$($OPENSSL x509 -in "${CERT}" -ocsp_uri -noout)"

    ISSUER_URI="$($OPENSSL x509 -in "${CERT}" -text -noout | grep "CA Issuers" | sed -e "s/^.*CA Issuers - URI://")"

    if [ -z "${ISSUER_URI}" ] ; then
	if [ -n "${VERBOSE}" ] ; then
	    echo "cannot find the CA Issuers in the certificate: disabling OCSP checks"
	fi
	OCSP=""
    fi

    SIGNATURE_ALGORITHM="$($OPENSSL x509 -in "${CERT}" -text -noout | grep 'Signature Algorithm' | head -n 1)"

    if [ -n "${DEBUG}" ] ; then
	echo "[DBG] " "$($OPENSSL x509 -in "${CERT}" -subject -noout -nameopt utf8,oneline,-esc_msb)"
	echo "[DBG] CN         = ${CN}"
	echo "[DBG] CA_O       = ${CA_O}"
	echo "[DBG] CA_CN      = ${CA_CN}"
	echo "[DBG] SERIAL     = ${SERIAL}"
	echo "[DBG] OCSP_URI   = ${OCSP_URI}"
	echo "[DBG] ISSUER_URI = ${ISSUER_URI}"
	echo "[DBG] ${SIGNATURE_ALGORITHM}"
    fi
    
    if echo "${SIGNATURE_ALGORITHM}" | grep -q "sha1" ; then

        if [ -n "${NOSIGALG}" ] ; then

            if [ -n "${VERBOSE}" ] ; then
                echo 'Certificate is signed with SHA-1'
            fi

        else

            critical 'Certificate is signed with SHA-1'

        fi

    fi

    if echo "${SIGNATURE_ALGORITHM}" | grep -qi "md5" ; then

        if [ -n "${NOSIGALG}" ] ; then

            if [ -n "${VERBOSE}" ] ; then
                echo 'Certificate is signed with MD5'
            fi

        else

            critical 'Certificate is signed with MD5'

        fi

    fi

    ################################################################################
    # Generate the long output
    if [ -n "${LONG_OUTPUT_ATTR}" ] ; then

        check_attr() {
            ATTR="$1"
            if ! echo "${VALID_ATTRIBUTES}" | grep -q ",${ATTR}," ; then
                unknown "Invalid certificate attribute: ${ATTR}"
            else
                value="$(${OPENSSL} x509 -in "${CERT}" -noout -nameopt utf8,oneline,-esc_msb  -"${ATTR}" | sed -e "s/.*=//")"
                LONG_OUTPUT="${LONG_OUTPUT}\n${ATTR}: ${value}"
           fi

        }

        # Split on comma
        if [ "${LONG_OUTPUT_ATTR}" = "all" ] ; then
            LONG_OUTPUT_ATTR="${VALID_ATTRIBUTES}"
        fi
        attributes=$( echo ${LONG_OUTPUT_ATTR} | tr ',' "\n" )
        for attribute in $attributes ; do
            check_attr "${attribute}"
        done

    fi

    ################################################################################
    # Compute for how many days the certificate will be valid
    if [ -n "${DATETYPE}" ]; then

        CERT_END_DATE=$($OPENSSL x509 -in "${CERT}" -noout -enddate | sed -e "s/.*=//")

        OLDLANG=$LANG
        LANG=en_US

	if [ -n "${DEBUG}" ] ; then
	    echo "[DBG] Date computations: ${DATETYPE}"
	fi

        case "${DATETYPE}" in
            "BSD")
                DAYS_VALID=$(( ( $(${DATEBIN} -jf "%b %d %T %Y %Z" "${CERT_END_DATE}" +%s) - $(${DATEBIN} +%s) ) / 86400 ))
                ;;

            "GNU")
                DAYS_VALID=$(( ( $(${DATEBIN} -d "${CERT_END_DATE}" +%s) - $(${DATEBIN} +%s) ) / 86400 ))
                ;;

            "PERL")
				DAYS_VALID=$(perl - "${CERT_END_DATE}" <<-"EOF"
					use strict;
					use warnings;
					use Date::Parse;
					my $cert_date = str2time( $ARGV[0] );
					my $days = int (( $cert_date - time ) / 86400 + 0.5);
					print "$days\n";
				EOF
				)
				;;
        esac

        LANG=$OLDLANG

        if [ -n "${VERBOSE}" ] ; then

            if [ "${DAYS_VALID}" -ge 0 ] ; then
                echo "The certificate will expire in ${DAYS_VALID} day(s)"
            else
                echo "The certificate expired "$((- DAYS_VALID))" day(s) ago"
            fi

        fi
        add_performance_data "days=$DAYS_VALID;${WARNING};${CRITICAL};;"

    fi

    ################################################################################
    # Check the CN
    if [ -n "$COMMON_NAME" ] ; then
	
        ok=""

        if [ -n "${DEBUG}" ] ; then
            echo "[DBG] check CN: ${CN}"
        fi

	# Common name is case insensitive: using grep for comparison (and not 'case' with 'shopt -s nocasematch' as not defined in POSIX
	
        if echo "${CN}" | grep -q -i "^\*\." ; then

            # Match the domain
            if [ -n "${DEBUG}" ] ; then
                echo "[DBG] the common name ${CN} begins with a '*'"
		echo "[DBG] checking if the common name matches ^$(echo "${CN}" | cut -c 3-)\$"
            fi	    
            if echo "${COMMON_NAME}" | grep -q -i "^$(echo "${CN}" | cut -c 3-)\$" ; then
                if [ -n "${DEBUG}" ] ; then
                    echo "[DBG] the common name ${COMMON_NAME} matches ^$( echo "${CN}" | cut -c 3- )\$"
                fi
                ok="true"

            fi

            # Or the literal with the wildcard
            if [ -n "${DEBUG}" ] ; then
		echo "[DBG] checking if the common name matches ^$(echo "${CN}" | sed -e 's/[.]/[.]/g' -e 's/[*]/[A-Za-z0-9\-]*/' )\$"
            fi
            if echo "${COMMON_NAME}" | grep -q -i "^$(echo "${CN}" | sed -e 's/[.]/[.]/g' -e 's/[*]/[A-Za-z0-9\-]*/' )\$" ; then
                if [ -n "${DEBUG}" ] ; then
                    echo "[DBG] the common name ${COMMON_NAME} matches ^$(echo "${CN}" | sed -e 's/[.]/[.]/g' -e 's/[*]/[A-Za-z0-9\-]*/' )\$"
                fi
                ok="true"
            fi

	    # Or if both are exactly the same
            if [ -n "${DEBUG}" ] ; then
		echo "[DBG] checking if the common name matches ^${CN}\$"
            fi
	    if echo "${COMMON_NAME}" | grep -q -i "^${CN}\$" ; then
                if [ -n "${DEBUG}" ] ; then
                    echo "[DBG] the common name ${COMMON_NAME} matches ^${CN}\$"
                fi
                ok="true"
            fi	    

        else

	    if echo "${COMMON_NAME}" | grep -q -i "^${CN}$" ; then
                ok="true"
	    fi
	    
        fi

        # Check alterante names
        if [ -n "${ALTNAMES}" ] ; then

            for cn in ${COMMON_NAME} ; do

                ok=""

                if [ -n "${DEBUG}" ] ; then
                    echo "[DBG] checking altnames against ${cn}"
                fi

                for alt_name in $($OPENSSL x509 -in "${CERT}" -text \
                    | grep --after-context=1 "509v3 Subject Alternative Name:" \
                    | tail -n 1 | sed -e "s/DNS://g" | sed -e "s/,//g") ; do

                    if [ -n "${DEBUG}" ] ; then
                        echo "[DBG]   ${alt_name}"
                    fi

		    if echo "${cn}" | grep -q -i "^${alt_name}$" ; then
                        ok="true"
		    fi

                done

                if [ -z "$ok" ] ; then
                    fail=$cn
                    break;
                fi

            done

        fi

        if [ -n "$fail" ] ; then
           critical "invalid CN ('$CN' does not match '$fail')"
        fi

        if [ -z "$ok" ] ; then
            critical "invalid CN ('$CN' does not match '$COMMON_NAME')"
        fi

        if [ -n "${DEBUG}" ] ; then
            echo "[DBG] CN check finished"
        fi

    fi

    ################################################################################
    # Check the issuer
    if [ -n "$ISSUER" ] ; then

        if [ -n "${DEBUG}" ] ; then
            echo "[DBG] check ISSUER: ${ISSUER}"
        fi

        ok=""
        CA_ISSUER_MATCHED=""

        if echo "$CA_CN" | grep -q "^${ISSUER}\$" ; then
            ok="true"
            CA_ISSUER_MATCHED="${CA_CN}"
        fi

        if echo "$CA_O" | grep -q "^${ISSUER}\$" ; then
            ok="true"
            CA_ISSUER_MATCHED="${CA_O}"
        fi

        if [ -z "$ok" ] ; then
            critical "invalid CA ('$ISSUER' does not match '$CA_O' or '$CA_CN')"
        fi

    else

        CA_ISSUER_MATCHED="${CA_CN}"

    fi

    ################################################################################
    # Check the serial number
    if [ -n "$SERIAL_LOCK" ] ; then

        ok=""

        if echo "$SERIAL" | grep -q "^${SERIAL_LOCK}\$" ; then
            ok="true"
        fi

        if [ -z "$ok" ] ; then
            critical "invalid serial number ('$SERIAL' does not match '$SERIAL_LOCK')"
        fi

    fi

    ################################################################################
    # Check the validity
    if [ -z "${NOEXP}" ] ; then

        # We always check expired certificates
        if ! $OPENSSL x509 -in "${CERT}" -noout -checkend 0 ; then
            critical "certificate is expired (was valid until $DATE)"
        fi

        if [ -n "${CRITICAL}" ] ; then

            if [ -n "${DEBUG}" ] ; then
                echo "[DBG] executing: $OPENSSL x509 -in ${CERT} -noout -checkend $(( CRITICAL * 86400 ))"
            fi

            if ! $OPENSSL x509 -in "${CERT}" -noout -checkend $(( CRITICAL * 86400 )) ; then
                critical "certificate will expire on $DATE"
            fi

        fi

        if [ -n "${WARNING}" ] ; then

            if [ -n "${DEBUG}" ] ; then
                echo "[DBG] executing: $OPENSSL x509 -in ${CERT} -noout -checkend $(( WARNING * 86400 ))"
            fi

            if ! $OPENSSL x509 -in "${CERT}" -noout -checkend $(( WARNING * 86400 )) ; then
                warning "certificate will expire on $DATE"
            fi

        fi

    fi

    ################################################################################
    # Check SSL Labs
    if [ -n "${SSL_LAB_ASSESTMENT}" ] ; then

        if [ -n "${VERBOSE}" ] ; then
            echo "Checking SSL Labs assestment"
        fi

        while true; do

            JSON="$(curl --silent "https://api.ssllabs.com/api/v2/analyze?host=${HOST}${IGNORE_SSL_LABS_CACHE}")"
            CURL_RETURN_CODE=$?

            if [ ${CURL_RETURN_CODE} -ne 0 ] ; then

                if [ -n "${DEBUG}" ] ; then
                    echo "[DBG] curl returned ${CURL_RETURN_CODE}: curl --silent \"https://api.ssllabs.com/api/v2/analyze?host=${HOST}${IGNORE_SSL_LABS_CACHE}\""
                fi

                unknown "Error checking SSL Labs: curl returned ${CURL_RETURN_CODE}, see 'man curl' for details"

            fi

            JSON="$(printf '%s' "${JSON}" | tr '\n' ' ' )"

            if [ -n "${DEBUG}" ] ; then
                echo "[DBG] Checking SSL Labs: curl --silent \"https://api.ssllabs.com/api/v2/analyze?host=${HOST}\""
                echo "[DBG] SSL Labs JSON: ${JSON}"
            fi

            # We clear the cache only on the first run
            IGNORE_SSL_LABS_CACHE=""

            SSL_LABS_HOST_STATUS=$(echo "${JSON}" \
                | sed 's/.*"status":[ ]*"\([^"]*\)".*/\1/')

            if [ -n "${DEBUG}" ] ; then
                echo "[DBG] SSL Labs status: ${SSL_LABS_HOST_STATUS}"
            fi

            case "${SSL_LABS_HOST_STATUS}" in
                'ERROR')
                    SSL_LABS_STATUS_MESSAGE=$(echo "${JSON}" \
                        | sed 's/.*"statusMessage":[ ]*"\([^"]*\)".*/\1/')
                    critical "Error checking SSL Labs: ${SSL_LABS_STATUS_MESSAGE}"
                    ;;
                'READY')
                    if ! echo "${JSON}" | grep -q "grade" ; then

                        # Something went wrong
                        SSL_LABS_STATUS_MESSAGE=$(echo "${JSON}" \
                            | sed 's/.*"statusMessage":[ ]*"\([^"]*\)".*/\1/')
                        critical "SSL Labs error: ${SSL_LABS_STATUS_MESSAGE}"

                    else

                        SSL_LABS_HOST_GRADE=$(echo "${JSON}" \
                            | sed 's/.*"grade":[ ]*"\([^"]*\)".*/\1/')

                        if [ -n "${DEBUG}" ] ; then
                            echo "[DBG] SSL Labs grade: ${SSL_LABS_HOST_GRADE}"
                        fi

                        if [ -n "${VERBOSE}" ] ; then
                            echo "SSL Labs grade: ${SSL_LABS_HOST_GRADE}"
                        fi

                        convert_ssl_lab_grade "${SSL_LABS_HOST_GRADE}"
                        SSL_LABS_HOST_GRADE_NUMERIC="${NUMERIC_SSL_LAB_GRADE}"

                        add_performance_data "ssllabs=${SSL_LABS_HOST_GRADE_NUMERIC}%;;${SSL_LAB_ASSESTMENT_NUMERIC}"

                        # Check the grade
                        if [ "${SSL_LABS_HOST_GRADE_NUMERIC}" -lt "${SSL_LAB_ASSESTMENT_NUMERIC}" ] ; then
                            critical "SSL Labs grade is ${SSL_LABS_HOST_GRADE} (instead of ${SSL_LAB_ASSESTMENT})"
                        fi

                        if [ -n "${DEBUG}" ] ; then
                            echo "[DBG] SSL Labs grade (converted): ${SSL_LABS_HOST_GRADE_NUMERIC}"
                        fi

                        # We have a result: exit
                        break

                    fi
                    ;;
                'IN_PROGRESS')
                    # Data not yet available: warn and continue
                    if [ -n "${VERBOSE}" ] ; then
                        echo "Warning: no cached data by SSL Labs, check initiated"
                    fi
                    ;;
                'DNS')
                    if [ -n "${VERBOSE}" ] ; then
                        echo 'SSL Labs cannot resolve the domain name'
                    fi
                    ;;
                *)
                    # Try to extract a message
                    SSL_LABS_ERROR_MESSAGE=$(echo "${JSON}" \
                        | sed 's/.*"message":[ ]*"\([^"]*\)".*/\1/')

                    if [ -z "${SSL_LABS_ERROR_MESSAGE}" ] ; then
                        SSL_LABS_ERROR_MESSAGE="${JSON}"
                    fi

                    critical "Cannot check status on SSL Labs: ${SSL_LABS_ERROR_MESSAGE}"
            esac

            WAIT_TIME=60
            if [ -n "${VERBOSE}" ] ; then
                echo "Waiting ${WAIT_TIME} seconds"
            fi

            sleep "${WAIT_TIME}"

        done

    fi

    ################################################################################
    # Check revocation via OCSP
    if [ -n "${OCSP}" ]; then

	if [ -n "${DEBUG}" ] ; then
	    echo "[DBG] OCSP: fetching issuer certificate ${ISSUER_URI} to ${ISSUER_CERT}"
	fi
	
        curl --silent "${ISSUER_URI}" > "${ISSUER_CERT}"

	if [ -n "${DEBUG}" ] ; then
	    echo "[DBG] OCSP: issuer certificate type: $(${FILE_BIN} "${ISSUER_CERT}" | sed 's/.*://' )"
	fi
	
	# check the result
	if ! "${FILE_BIN}" "${ISSUER_CERT}" | grep -q ': (ASCII|PEM)' ; then
	
            if "${FILE_BIN}" "${ISSUER_CERT}" | grep -q ': data' ; then
		
		if [ -n "${DEBUG}" ] ; then
		    echo "[DBG] OCSP: converting issuer certificate from DER to PEM"
		fi
		
		openssl x509 -inform DER -outform PEM -in "${ISSUER_CERT}" -out "${ISSUER_CERT}"

	    else

		unknown "Unable to fetch OCSP issuer certificate."

	    fi
		
		
        fi

	if [ -n "${DEBUG}" ] ; then

	    # remove trailing /
	    FILE_NAME=${ISSUER_URI%/}

	    # remove everything up to the last slash
	    FILE_NAME=${FILE_NAME##*/}
	    
	    echo "[DBG] OCSP: storing a copy of the retrieved issuer certificate to ${FILE_NAME}"
	    
	    cp "${ISSUER_CERT}" "${FILE_NAME}"
	fi
	
        OCSP_HOST="$(echo "${OCSP_URI}" | sed -e "s@.*//\([^/]\+\)\(/.*\)\?\$@\1@g" | sed 's/^http:\/\///' | sed 's/\/.*//' )"

	if [ -n "${DEBUG}" ] ; then
	    echo "[DBG] OCSP: host = ${OCSP_HOST}"
	fi

	# check if -header is supported
	OCSP_HEADER=""

	# oscp -header is supported in OpenSSL versions from 1.0.0, but not documented until 1.1.0
	# so we check if the major version is greater than 0
	if [ "$( ${OPENSSL} version | sed -e 's/OpenSSL \([0-9]\).*/\1/g' )"  -gt 0 ]; then

	    if [ -n "${DEBUG}" ] ; then
		echo "[DBG] openssl ocsp support the -header option"
	    fi
	    
	    # http_proxy is sometimes lower- and sometimes uppercase. Programs usually check both
	    # shellcheck disable=SC2154
	    if [ -n "${http_proxy}" ] ; then
		HTTP_PROXY="${http_proxy}"
	    fi

            if [ -n "${HTTP_PROXY:-}" ] ; then
		
		if [ -n "${DEBUG}" ] ; then
		    echo "[DBG] executing $OPENSSL ocsp -no_nonce -issuer ${ISSUER_CERT} -cert ${CERT} -host ${HTTP_PROXY#*://} -path ${OCSP_URI} -header HOST ${OCSP_HOST}"
		fi

                OCSP_RESP="$($OPENSSL ocsp -no_nonce -issuer "${ISSUER_CERT}" -cert "${CERT}" -host "${HTTP_PROXY#*://}" -path "${OCSP_URI}" -header HOST "${OCSP_HOST}" 2>&1 | grep -i "ssl_cert")"

            else
		
		if [ -n "${DEBUG}" ] ; then
		    echo "[DBG] executing $OPENSSL ocsp -no_nonce -issuer ${ISSUER_CERT} -cert ${CERT}  -url ${OCSP_URI} ${OCSP_HEADER} -header HOST ${OCSP_HOST}"
		fi

                OCSP_RESP="$($OPENSSL ocsp -no_nonce -issuer "${ISSUER_CERT}" -cert "${CERT}" -url "${OCSP_URI}" -header HOST "${OCSP_HOST}" 2>&1 | grep -i "ssl_cert")"


            fi
	    
	    if [ -n "${DEBUG}" ] ; then
		echo "[DBG] OCSP: response = ${OCSP_RESP}"	
	    fi
	    
            if echo "${OCSP_RESP}" | grep -qi "revoked" ; then
		critical "certificate is revoked"
            elif ! echo "${OCSP_RESP}" | grep -qi "good" ; then	    
	    
                if [ -n "${HTTP_PROXY:-}" ] ; then
                    OCSP_RESP="$($OPENSSL ocsp -no_nonce -issuer "${ISSUER_CERT}" -cert "${CERT}" -host "${HTTP_PROXY#*://}" -path "${OCSP_URI}" "${OCSP_HEADER}" 2>&1 )"
                else
                    OCSP_RESP="$($OPENSSL ocsp -no_nonce -issuer "${ISSUER_CERT}" -cert "${CERT}" -url "${OCSP_URI}" "${OCSP_HEADER}" 2>&1 )"
                fi
		critical "${OCSP_RESP}"
	    
	    fi
	    
	else
	    
	    if [ -n "${VERBOSE}" ] ; then
		echo "openssl ocsp does not support the -header option: disabling OCSP checks"
	    fi

	fi


    fi

    ################################################################################
    # Check the organization
    if [ -n "$ORGANIZATION" ] ; then

        ORG=$($OPENSSL x509 -in "${CERT}" -subject -noout | sed -e "s/.*\/O=//" -e "s/\/.*//")

        if ! echo "$ORG" | grep -q "^$ORGANIZATION" ; then
            critical "invalid organization ('$ORGANIZATION' does not match '$ORG')"
        fi

    fi

    ################################################################################
    # Check the organization
    if [ -n "$ADDR" ] ; then

        EMAIL=$($OPENSSL x509 -in "${CERT}" -email -noout)

        if [ -n "${VERBOSE}" ] ; then
            echo "checking email (${ADDR}): ${EMAIL}"
        fi

        if [ -z "${EMAIL}" ] ; then
            critical "the certificate does not contain an email address"
        fi

        if ! echo "$EMAIL" | grep -q "^$ADDR" ; then
            critical "invalid email ($ADDR does not match $EMAIL)"
        fi

    fi

    ################################################################################
    # Check if the certificate was verified
    if [ -z "${NOAUTH}" ] && grep -q '^verify\ error:' "${ERROR}" ; then

        if grep -q '^verify\ error:num=[0-9][0-9]*:self\ signed\ certificate' "${ERROR}" ; then

            if [ -z "${SELFSIGNED}" ] ; then
                critical "Cannot verify certificate, self signed certificate"
            else
                SELFSIGNEDCERT="self signed "
            fi

        else

            if [ -n "${DEBUG}" ] ; then
                sed 's/^/[DBG] Error: /' "${ERROR}"
            fi

            # Process errors
            details=$( grep  '^verify\ error:' "${ERROR}" | sed 's/verify\ error:num=[0-9]*://' | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/, /g' )
            critical "Cannot verify certificate: ${details}"

        fi

    fi

    ################################################################################
    # If we get this far, assume all is well. :)

    # If --altnames was specified we show the specified CN instead of
    # the certificate CN
    if [ -n "${ALTNAMES}" ] && [ -n "${COMMON_NAME}" ] ; then
        CN=${COMMON_NAME}
    fi

    if [ -n "${DAYS_VALID}" ] ; then
        # nicer formatting
        if [ "${DAYS_VALID}" -gt 1 ] ; then
            DAYS_VALID=" (expires in ${DAYS_VALID} days)"
        elif [ "${DAYS_VALID}" -eq 1 ] ; then
            DAYS_VALID=" (expires tomorrow)"
        elif [ "${DAYS_VALID}" -eq 0 ] ; then
            DAYS_VALID=" (expires today)"
        elif [ "${DAYS_VALID}" -eq -1 ] ; then
            DAYS_VALID=" (expired yesterday)"
        else
            DAYS_VALID=" (expired ${DAYS_VALID} days ago)"
        fi
    fi

    if [ -n "${SSL_LABS_HOST_GRADE}" ] ; then
        SSL_LABS_HOST_GRADE=", SSL Labs grade: ${SSL_LABS_HOST_GRADE}"
    fi

    echo "${SHORTNAME} OK - X.509 ${SELFSIGNEDCERT}certificate for '${CN}' from '${CA_ISSUER_MATCHED}' valid until ${DATE}${DAYS_VALID}${SSL_LABS_HOST_GRADE}${PERFORMANCE_DATA}${LONG_OUTPUT}"

    exit 0

}

if [ -z "${SOURCE_ONLY}" ]; then
    main "${@}"
fi
