#!/bin/sh
###
### Check name and date in ssl certificate
###

ExitStatus=0
ShowCert="NO"
Feedback=NORMAL
ExpireWarn=30
ExpireFail=0
Names=""
CAdir=@SIPX_CONFDIR@/ssl/authorities

while [ $# -ne 0 ]
do
    case ${1} in
        ##
        ## Verbose
        ##
        -v|--verbose)
            Feedback=VERBOSE
            ;;

        ##
        ## 
        ##
        -w|--warning)
            if [ $# -lt 2 ]
            then
                echo "Must specify <warning-days> with ${1}" 1>&2
                Action=USAGE
                break
            else
                ExpireWarn=${2}
                shift # consume the switch
            fi
            ;;

        -f|--fail)
            if [ $# -lt 2 ]
            then
                echo "Must specify <failure-days> with ${1}" 1>&2
                Action=USAGE
                break
            else
                ExpireFail=${2}
                shift # consume the switch
            fi
            ;;

        -n|--name)
            if [ $# -lt 2 ]
            then
                echo "Must specify <name> with ${1}" 1>&2
                Action=USAGE
                break
            else
                Names="${Names} ${2}"
                shift # consume the switch
            fi
            ;;

        -a|--authority)
            if [ $# -lt 2 ]
            then
                echo "Must specify <authority-file> with ${1}" 1>&2
                Action=USAGE
                break
            else
                Authority=${2}
                shift # consume the switch
            fi
            ;;

        ##
        ## handle the 'end of options' marker
        ##
        --)
            ;;

        ##
        ## handle an unknown switch
        ##
        -*)
            Action=USAGE
            break
            ;;

        *)
            if [ -z "${Certificate}" ]
            then
                Certificate=${1}
            else
                echo "Too many arguments supplied: $@" 1>&2
                Action=USAGE
                break
            fi
            ;;
    esac           

    shift # always consume 1
done

if [ "${Action}" = "USAGE" -o -z "${Certificate}" ]
then
    cat <<USAGE

Usage:
    
    check-cert [ -v | --verbose ]
               [ {-w | --warning} <warning-days> ]
               [ {-f | --fail} <failure-days> ]
               [ {-n | --name} <name> ]...
               [ {-a | --authority} <authority-file> ]...
               <certificate-file>

    Checks signature validity, name, and expiration of certificate.

    Prints a warning if the certificate will expire
      in less than <warning-days> (default is 30)

    Returns failure (and prints message) if the certificate expires 
      in less than <failure-days> (default is 0)

    Always returns a failure if the certificate has expired.
    
    The --name option may be repeated any number of times, and if it is
    given, the commonName attribute of the certificate must equal one of
    the names.

    The acceptable certificate authorities must be installed in the
         ${CAdir}
    directory unless the --authority switch is used to point to an
    authority certificate.
USAGE
    exit
fi

if [ -d ${CAdir} ]
then
    authorities=`find ${CAdir} \( -name \*.crt -o -name \*.crl -o -name \*.pem \) 2> /dev/null`
    if [ -n "${authorities}" ]
    then
        AuthSwitches="-CApath ${CAdir}"
    fi
fi

if [ -f "${Authority}" ]
then 
    AuthSwitches="${AuthSwitches} -CAfile ${Authority}"
fi

if [ -z "${AuthSwitches}" ]
then
    cat 1>&2 <<EOF
    No certificate authorities found - cannot validate certificate 
     Authorities directory: ${CAdir}
     Authority certificate: ${Authority}
EOF
    ExitStatus=1
fi

if [ -r ${Certificate} ]
then
    
    # Validate the signatures and issuers
    #   (also checks expiration, but does not provide advance warning as below)
    cat /dev/null > /tmp/check-cert.$$
    @OPENSSL@ verify ${AuthSwitches} -purpose sslclient \
         ${Certificate} \
         > /tmp/check-cert.$$ 2>&1
    if ! echo "${Certificate}: OK" | diff - /tmp/check-cert.$$ > /dev/null 2>&1
    then
        echo "SSL Certificate '${Certificate}' is invalid as client certificate." 1>&2
        sed 's/^/    /' /tmp/check-cert.$$ 1>&2
        ExitStatus=1
    fi
    cat /dev/null > /tmp/check-cert.$$
    @OPENSSL@ verify ${AuthSwitches} -purpose sslserver \
         ${Certificate} \
         > /tmp/check-cert.$$ 2>&1
    if ! echo "${Certificate}: OK" | diff - /tmp/check-cert.$$ > /dev/null 2>&1
    then
        echo "SSL Certificate '${Certificate}' is invalid as server certificate." 1>&2
        sed 's/^/    /' /tmp/check-cert.$$ 1>&2
        ExitStatus=1
    fi
    rm -f /tmp/check-cert.$$

    # Check subject name if any correct answers are provided
    cert_name=`@OPENSSL@ x509 -in "${Certificate}" -subject -nameopt RFC2253,multiline -noout | perl -ne 'use English; m/^ +commonName += / && print $POSTMATCH'`

    if [ -n "${Names}" ]
    then
        UniqueNames=`for n in ${Names}; do echo $n; done | sort -u`
        Matched="NO"
        for name in ${UniqueNames}
        do 
          if [ "${name}" = "${cert_name}" ]; then Matched="YES"; fi
        done
        if [ "${Matched}" = "NO" ]
        then
            echo -n "SSL certificate name '${cert_name}' is not one of: " 1>&2
            for name in ${UniqueNames}; do echo -n "'${name}' " 1>&2; done
            echo "" 1>&2
            ExitStatus=1
            ShowCert="SHOW"
        fi
    fi

    # Check expiration
    warnSeconds=$((${ExpireWarn} * 3600 * 24))
    failSeconds=$((${ExpireFail} * 3600 * 24))

    cert_expires=`@OPENSSL@ x509 -in "${Certificate}" -noout -enddate | sed 's/notAfter=//'`
    now=`date +%s`
    if [ $? -eq 0 ] # date command support epoch format
    then
        exp=`date -j -f "%b %d %T %Y %Z" "${cert_expires}" +%s`
        remaining=$(($exp - $now))

        if [ ${remaining} -le 0 ] # cert expired
        then
            echo "SSL certificate expired: ${cert_expires}" 1>&2
            ExitStatus=1

        elif [ ${failSeconds} -gt 0 -a ${remaining} -le ${failSeconds} ]
        then
            echo "SSL certificate expires in less than ${ExpireFail} days: ${cert_expires}" 1>&2
            ExitStatus=1

        elif [ ${remaining} -le ${warnSeconds} ]
        then
            echo "SSL certificate expires in less than ${ExpireWarn} days: ${cert_expires}" 1>&2
            ShowCert="SHOW"

        fi
    else
        echo "Your 'date' command does not support %s format - cannot calculate expiration." 1>&2
        echo "SSL certificate expires: ${cert_expires}" 1>&2
        ShowCert="SHOW"
    fi

    if [ "${Feedback}" = "VERBOSE" ]
    then
        caName=`@OPENSSL@ x509 -in "${Certificate}" -issuer -nameopt RFC2253,multiline -noout | perl -ne 'use English; m/^ +commonName += / && print $POSTMATCH'`

        echo "SSL certificate name:    ${cert_name}" 
        echo "SSL certificate issuer:  ${caName}" 
        echo "SSL certificate expires: ${cert_expires}" 
    fi

else
    echo "SSL certificate not found." 1>&2
    ExitStatus=1
fi

if [ ${ExitStatus} -ne 0 -o "${ShowCert}" = "SHOW" ]
then
    echo "SSL certificate: ${Certificate}" 1>&2
fi

exit ${ExitStatus}
# DUMP x509v3 = @OPENSSL@ x509 -in /opt/ssl-sipit/ssl.crt -text -certopt ca_default -certopt no_sigdump -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_signame -noout
#         X509v3 extensions:
#             X509v3 Basic Constraints: 
#             CA:FALSE
#             X509v3 Subject Alternative Name: 
#             URI:sip:pt.sipit.net, DNS:scott.pt.sipit.net
#             X509v3 Subject Key Identifier: 
#             C1:54:B1:12:21:E5:70:ED:93:26:57:38:97:A9:CA:B4:8B:D4:74:D2


syntax highlighted by Code2HTML, v. 0.9.1