Listing 2 ssl-cert-check.sh
#!/bin/bash
#
# Program: SSL Certificate Check <ssl-cert-check>
#
# Author: Ryan Matteson <matty@daemons.net>
#
# Current Version: 1.3
#
# Revision History:
#
# Version 1.3
# Updated the prints messages to display the reason a connection
# failed (connection refused, connection timesout, bad cert, etc)
#
# Updated the GNU date checking routines. I really wish UNIX/BSD/Linux
# based systems utilized a common set of options/interfaces. *sigh*
#
# Version 1.2
# Added checks for each binary required
# Added checks for connection timeouts
#
# Versions 1.1
# Added checks for GNU date
# Added a "-h" option
# Cleaned up the documentation
#
# Version 1.0
# Initial Release
#
# Last Updated: 8-20-2004
#
# Purpose:
# ssl-cert-check checks to see if a digital certificate in X.509 format
# 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, 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Requirements:
# Requires openssl and GNU date
#
# Installation:
# Copy the shell script to a suitable location
#
# Usage (script should be run as a non-privileged user):
# Usage: ./ssl-cert-check {{ [ -b ] && [ -f cert_file ] } || { [ -s common_name ] && [ -p port] }}
# [ -e email ] [ -x expir_days ] [ -q ] [ -a ] [ -h ]
# -a : Send a warning message through email
# -b : Print the expiration date for all certificates in cert_file (batch mode)
# -e email address : Email address to send expiration notices
# -f cert file : File with a list of common names and ports (eg., mail.daemons.net 443)
# -h : Print this screen
# -p port : Port to connect to (interactive mode)
# -s commmon name : Server to connect to (interactive mode)
# -q : Don't print anything on the console
# -x days : Certificate expiration interval (eg. if cert_date < days)
#
# Examples:
# Print the certificate expiration dates for a list of domains specified in the file ssldomains:
# $ ssl-cert-check -b -f ssldomains
#
# Print the certificate expiration date for mail.daemons.net:
# $ ssl-cert-check -s mail.daemons.net -p 995
#
# Run ssl-cert-check in quiet mode, check for all certificates that will
# expire in 75-days or less. Email the expiring certs to matty@daemons.net:
# $ ssl-cert-check -a -b -f ssldomains -q -x 75 -e matty@daemons.net
#
PATH=/bin:/usr/bin:/usr/local/bin:/usr/local/ssl/bin ; export PATH
# Who to page when an expired certificate is detected (cmdline: -e)
ADMIN="sysadmin@mydomain.com.1"
# Number of days to give as a buffer (cmdline: -x)
WARNDAYS=30
# If QUIET is set to TRUE, don't print anything on the console (cmdline: -q)
QUIET="FALSE"
# Don't send emails by default (cmdline: -a)
ALARM="FALSE"
# Location of system binaries
DATE="/export/home/matty/bin/date"
MAIL="/bin/mail"
OPENSSL="/usr/local/ssl/bin/openssl"
# Place to stash temporary files
CERT_TMP="$HOME/cert.$$"
ERROR_TMP="$HOME/error.$$"
# Set the default umask to be somewhat restrictive
umask 077
# Converts a date in string format passed as $1 into
# the number of seconds since 01/01/70 00:00:00 UTC.
# Replace this with another date normalization routine if
# you do not have GNU date available.
utcseconds() {
utcsec=`${DATE} -d "$1" +%s`
echo "$utcsec"
}
# Calculate the number of seconds between two dates
date_diff() {
diff=`expr $2 - $1`
echo "${diff}"
}
# Calculate days given seconds
date_days() {
DAYS=`expr ${1} \/ 86400`
echo "${DAYS}"
}
# Method used to print a line with a certificates expiration status
prints() {
if [ "${QUIET}" != "TRUE" ]
then
MIN_DATE=`echo $4 | awk '{ print $1, $2, $4 }'`
printf "%-30s %-25s %-20s %-5s\n" "$1:$2" "$3" "$MIN_DATE" "$5"
fi
}
# Print out a heading
print_heading() {
if [ "${QUIET}" != "TRUE" ]
then
printf "\n%-30s %-25s %-20s %-5s\n" "Host" "Status" "Expires" "Days Left"
fi
}
# Provide a listing of how the tool works
useage() {
echo "Usage: $0 {{ [ -b ] && [ -f cert_file ] } || { [ -s common_name ] && [ -p port] }}"
echo " [ -e email ] [ -x expir_days ] [ -q ] [ -a ] [ -h ]"
echo " -a : Send a warning message through email "
echo " -b : Print the expiration date for all certificates in cert_file (batch mode)"
echo " -e email address : Email address to send expiration notices"
echo " -f cert file : File with a list of common names and ports (eg., blatch.com 443)"
echo " -h : Print this screen"
echo " -p port : Port to connect to (interactive mode)"
echo " -s commmon name : Server to connect to (interactive mode)"
echo " -q : Don't print anything on the console"
echo " -x days : Certificate expiration interval (eg. if cert_date < days)"
}
# This method takes a HOST ($1) and PORT ($2) and checks to see
# if the certificate has expired
check_cert() {
echo "" | ${OPENSSL} s_client -connect ${1}:${2} 2> ${ERROR_TMP} 1> ${CERT_TMP}
if grep -i "Connection refused" ${ERROR_TMP} > /dev/null
then
prints ${1} ${2} "Connection refused" "?" "?"
elif grep -i "gethostbyname failure" ${ERROR_TMP} > /dev/null
then
prints ${1} ${2} "Cannot resolve domain" "?" "?"
elif grep -i "Operation timed out" ${ERROR_TMP} > /dev/null
then
prints ${1} ${2} "Operation timed out" "?" "?"
elif grep -i "ssl handshake failure" ${ERROR_TMP} > /dev/null
then
prints ${1} ${2} "SSL handshake failed" "?" "?"
elif grep -i "connect: Connection timed out" ${ERROR_TMP} > /dev/null
then
prints ${1} ${2} "Connection timed out" "?" "?"
else
# Get the expiration date with openssl
CERTDATE=`${OPENSSL} x509 -in ${CERT_TMP} -enddate -noout | sed 's/notAfter\=//'`
# Convert the date to seconds, and get the diff between NOW and the expiration date
CERTUTC=`utcseconds "${CERTDATE}"`
CERTDIFF=`date_diff ${NOWUTC} ${CERTUTC}`
if [ ${CERTDIFF} -lt 0 ]
then
if [ "${ALARM}" == "TRUE" ]
then
echo "The SSL certificate for ${1} has expired!" \
| ${MAIL} -s "Certificate for ${1} has expired!" ${ADMIN}
fi
DAYS=`date_days $CERTDIFF`
prints ${1} ${2} "Expired" "$CERTDATE" "$DAYS"
elif [ ${CERTDIFF} -lt ${WARNSECS} ]
then
if [ "${ALARM}" == "TRUE" ]
then
echo "The SSL certificate for ${1} will expire on ${CERTDATE}" \
| ${MAIL} -s "$0: Certificate for ${1} will expire in ${WARNDAYS}-days or less" ${ADMIN}
fi
DAYS=`date_days $CERTDIFF`
prints ${1} ${2} "Expiring" "${CERTDATE}" "${DAYS}"
else
DAYS=`date_days $CERTDIFF`
prints ${1} ${2} "Valid" "${CERTDATE}" "${DAYS}"
fi
fi
}
while getopts abe:f:hp:s:qx: option
do
case "${option}"
in
a) ALARM="TRUE";;
b) REPORT_ALL="TRUE";;
e) ADMIN=${OPTARG};;
f) SERVERFILE=$OPTARG;;
h) useage
exit 1;;
p) PORT=$OPTARG;;
s) HOST=$OPTARG;;
q) QUIET="TRUE";;
x) WARNDAYS=$OPTARG;;
\?) useage
exit 1;;
esac
done
if [ ! -f ${OPENSSL} ]
then
echo "ERROR: $OPENSSL does not exist. Please modify the \$OPENSSL variable."
exit 1
fi
if [ ! -f ${DATE} ]
then
echo "ERROR: $DATE does not exist. Please modify the \$DATE variable."
exit 1
fi
if [ ! -f ${MAIL} ]
then
echo "ERROR: $MAIL does not exist. Please modify the \$MAIL variable."
exit 1
fi
if ${DATE} -R -d "Wed Jan 1 00:00:00 EDT 2000" "+%s" 2>&1 | grep -i "illegal option" > /dev/null
then
echo "ERROR: \$DATE does not point to GNU date"
exit 1
fi
if ${DATE} -R -d "Wed Jan 1 00:00:00 EDT 2000" "+%s" 2>&1 | grep -i "unknown option" > /dev/null
then
echo "ERROR: \$DATE does not point to GNU date"
exit 1
fi
# Baseline the dates so we have something to compare with
NOWDATE=`${DATE}`
NOWUTC=`utcseconds "${NOWDATE}"`
# Get the number of seconds to compare the UTC time with
WARNSECS=`expr ${WARNDAYS} \* 86400`
# Touch the files to avoid issues
touch ${CERT_TMP} ${ERROR_TMP}
# IF a HOST and PORT were passed on the cmdline, use that
if [ "${HOST}" != "" ] && [ "${PORT}" != "" ]
then
print_heading
check_cert "${HOST}" "${PORT}"
# If a file and a "-a" are passed on the command line, check all
# of the certificates in the file to see if they are about to expire
elif [ "${REPORT_ALL}" == "TRUE" ] && [ -f "${SERVERFILE}" ]
then
print_heading
while read HOST PORT
do
check_cert "${HOST}" "${PORT}"
done < ${SERVERFILE}
# There was an error, so print how the tool works
else
useage
fi
# Remove the temporary files
rm -f ${CERT_TMP} ${ERROR_TMP}
|