#!/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, 2008 ETH Zurich.
#
# 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.
#
# RCS information
# enable substitution with:
#   $ svn propset svn:keywords "Id Revision HeadURL Source Date"
#
#   $Id: check_ssl_cert 1008 2008-05-13 10:41:24Z corti $
#   $Revision: 1008 $
#   $HeadURL: https://svn.id.ethz.ch/nagios_plugins/check_ssl_cert/check_ssl_cert $
#   $Date: 2008-05-13 12:41:24 +0200 (Tue, 13 May 2008) $

VERSION=1.2.2

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

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

    if [ -n "$1" ] ; then
        echo "Error: $1" 1>&2
    fi
    
    echo
    echo "Usage: check_ssl_cert -H server [OPTIONS]"
    echo
    echo "Options:"
    echo "   -d days    minimum number of days a certificate has to be valid"
    echo "   -e address pattern to match the email address contained in the certificate"
    echo "   -h, -?     this help message"
    echo "   -H host    server"
    echo "   -i issuer  pattern to match the issuer of the certificate"
    echo "   -o org     pattern to match the organization of the certificate"
    echo "   -p port    TCP port"
    echo "   -v         verbose output"
    echo "   -V         version"
    echo
    echo "Report bugs to: Matteo Corti <matteo.corti@id.ethz.ch>"
    echo

    exit 3

}

################################################################################
# Exits with a critical message
# Params
#   $1 error message
critical() {
    echo "SSL CERT CRITICAL $CN: $1"
    exit 2
}

################################################################################
# Exits with a warning message
# Param
#   $1 warning message
warning() {
    echo "SSL CERT WARN $CN: $1"
    exit 1
}

################################################################################
# Checks if a given program is available and executable
# Params
#   $1 program name
check_prog() {

    PROG=$(which $1)

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

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

}

################################################################################
# Main
################################################################################

# process command line options
PORT=443
while getopts "vh?H:Vp:d:i:o:e:" opt; do
    case $opt in
        d )      DAYS=$OPTARG;        ;;
        e )      ADDR=$OPTARG;        ;;
        h | \? ) usage ; exit 3;      ;;
        i )      ISSUER=$OPTARG;      ;;
        o )      ORGANIZATION=$OPTARG ;;
        v )      VERBOSE=1;           ;;
        V )      echo "check_ssl_cert version ${VERSION}"; exit 3; ;;
        H )      HOST=$OPTARG;        ;;
        p )      PORT=$OPTARG;        ;;
    esac
done
shift $(($OPTIND - 1))

################################################################################
# sanity checks

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

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

check_prog openssl
OPENSSL=$PROG

################################################################################
# fetch the X.509 certificate

CERT=$( mktemp -t "$( basename $0 )XXXXXX" )
ERROR=$( mktemp -t "$( basename $0 )XXXXXX" )
trap "rm -f $CERT $ERROR" 0

echo 'Q' | $OPENSSL s_client -connect $HOST:$PORT -servername $HOST 2> ${ERROR} 1> ${CERT}

if [ $? -ne 0 ] ; then
    critical "Error: $(cat $ERROR | head -n 1)"
fi

if ! grep -q "CERTIFICATE" ${CERT} ; then
    critical "No certificate returned"
fi

################################################################################
# parse the X.509 certificate

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

################################################################################
# check the issuer

if [ -n "$ISSUER" ] ; then
    if ! echo $CA | grep -q "^$ISSUER$" ; then
        critical "invalid CA ($ISSUER does not match $CA)"
    fi
fi

################################################################################
# check the validity

if [ -n "$DAYS" ] ; then

    #if ! echo $DAYS | grep -q [1-9][0-9]* ; then
    #    critical "invalid number of days ($DAYS)"
    #fi

    if ! $OPENSSL x509 -in ${CERT} -noout -checkend 0 > /dev/null ; then
        critical "certificate is expired (was valid until $DATE)"
    fi

    if ! $OPENSSL x509 -in ${CERT} -noout -checkend $(( $DAYS * 86400 )) > /dev/null ; then
        critical "certificate will expire on $DATE"
    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 ! echo $EMAIL | grep -q "^$ADDR" ; then
        critical "invalid email ($ADDR does not match $EMAIL)"
    fi

fi

################################################################################
# If we get this far, assume all is well. :)
echo "SSL CERT OK - X.509 certificate for $CN from $CA valid until $DATE"

exit 0
