- add /usr/bin/certwatch

- support --days argument to genkey (#131045)
This commit is contained in:
jorton 2004-09-10 14:16:06 +00:00
parent 07af1dc964
commit 51f1bc07ed
5 changed files with 339 additions and 9 deletions

182
certwatch.c Normal file
View File

@ -0,0 +1,182 @@
/*
Copyright 2003 Red Hat, Inc.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Certificate expiry warning generation code, based on code from
* Stronghold. Joe Orton <jorton@redhat.com> */
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Turn an ASN.1 UTCTIME object into a time_t, ish. */
static time_t decode_utctime(const ASN1_UTCTIME *utc)
{
struct tm tm = {0};
int i = utc->length;
if (i < 10)
return -1;
for (i = 0; i < 10; i++)
if ((utc->data[i] > '9') || (utc->data[i] < '0'))
return -1;
tm.tm_year = (utc->data[0]-'0') * 10 + (utc->data[1]-'0');
/* Deal with Year 2000 like eay did */
if (tm.tm_year < 70)
tm.tm_year += 100;
tm.tm_mon = (utc->data[2]-'0') * 10 + (utc->data[3]-'0') - 1;
tm.tm_mday = (utc->data[4]-'0') * 10 + (utc->data[5]-'0');
tm.tm_hour = (utc->data[6]-'0') * 10 + (utc->data[7]-'0');
tm.tm_min = (utc->data[8]-'0') * 10 + (utc->data[9]-'0');
tm.tm_sec = (utc->data[10]-'0') * 10 + (utc->data[11]-'0');
tm.tm_isdst = -1;
return mktime(&tm);
}
/* Print a warning message that the certificate in 'filename', issued
* to hostname 'hostname', will expire (or has expired). */
static int warning(FILE *out, const char *filename, const char *hostname,
time_t start, time_t end, time_t now, int quiet)
{
int renew = 1, days = (end - now) / (3600 * 24); /* days till expiry */
char subj[50];
if (start > now) {
strcpy(subj, "is not yet valid");
renew = 0;
} else if (days < 0) {
strcpy(subj, "has expired");
} else if (days == 0) {
strcpy(subj, "will expire today");
} else if (days == 1) {
sprintf(subj, "will expire tomorrow");
} else if (days < 30) {
sprintf(subj, "will expire in %d days", days);
} else {
return 0; /* nothing to warn about. */
}
if (quiet) return 1;
fputs("To: root@localhost\n", out);
fprintf(out, "Subject: The certificate for %s %s\n", hostname, subj);
fputs("\n", out);
fprintf(out,
" ################# SSL Certificate Warning ################\n\n");
fprintf(out, " Certificate for %s, in '%s':\n\n", hostname, filename);
if (renew) {
fputs(" The certificate needs to be renewed; this can be done\n"
" using the 'genkey' program supplied with Red Hat\n"
" Enterprise Linux.\n\n"
" Browsers will not be able to correctly connect to this\n"
" web site using SSL until the certificate is renewed.\n",
out);
} else {
char until[30] = "(unknown date)";
ctime_r(&start, until);
if (strlen(until) > 2) until[strlen(until)-1] = '\0';
fprintf(out,
" The certificate is not valid until %s.\n\n"
" Browsers will not be able to correctly connect to this\n"
" web site using SSL until the certificate becomes valid.\n",
until);
}
fputs("\n"
" ##########################################################\n"
" Generated by certwatch(8)\n\n",
out);
return 1;
}
/* Extract the common name of 'cert' into 'buf'. */
static int get_common_name(X509 *cert, char *buf, size_t bufsiz)
{
X509_NAME *name = X509_get_subject_name(cert);
if (!name) return -1;
return X509_NAME_get_text_by_NID(name, NID_commonName, buf, bufsiz) == -1;
}
/* Check whether the certificate in filename 'filename' has expired;
* issue a warning message if 'quiet' is zero. If quiet is non-zero,
* returns one to indicate that a warning would have been issued, zero
* to indicate no warning would be issued, or -1 if an error
* occurred. */
static int check_cert(const char *filename, int quiet)
{
X509 *cert;
FILE *fp;
ASN1_UTCTIME *notAfter, *notBefore;
time_t begin, end, now;
char cname[128];
/* parse the cert */
if ((fp = fopen(filename, "r")) == NULL) return -1;
cert = PEM_read_X509(fp, NULL, NULL, NULL);
fclose(fp);
if (cert == NULL) return -1;
/* determine the validity period of the cert. */
notAfter = X509_get_notAfter(cert);
notBefore = X509_get_notBefore(cert);
/* get time_t's out of X509 times */
begin = decode_utctime(notBefore);
end = decode_utctime(notAfter);
now = time(NULL);
if (end == -1 || begin == -1 || now == -1) return -1;
/* find the subject's commonName attribute */
if (get_common_name(cert, cname, sizeof cname))
return -1;
X509_free(cert);
/* don't warn about the automatically generate certificate */
if (strcmp(cname, "localhost") == 0 ||
strcmp(cname, "localhost.localdomain") == 0)
return -1;
return warning(stdout, filename, cname, begin, end, now, quiet);
}
int main(int argc, char **argv)
{
int quiet = 0;
if (argc == 3 && strcmp(argv[1], "-q") == 0) {
quiet = 1;
argc--;
argv++;
}
if (argc != 2) return 0;
return check_cert(argv[1], quiet) == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
}

29
certwatch.cron Normal file
View File

@ -0,0 +1,29 @@
#!/bin/sh
#
# Issue warning e-mails if SSL certificates expire, using
# certwatch(8). Set NOCERTWATCH=yes in /etc/sysconfig/httpd
# to disable.
#
[ -r /etc/sysconfig/httpd ] && . /etc/sysconfig/httpd
# Use configured httpd binary
httpd=${HTTPD-/usr/sbin/httpd}
# Sanity checks
test -z "${NOCERTWATCH}" || exit 0
test -x ${httpd} || exit 0
test -x /usr/bin/certwatch || exit 0
test -r /etc/httpd/conf/httpd.conf || exit 0
test -x /usr/sbin/sendmail || exit 0
test -x /etc/httpd/modules/mod_ssl.so || exit 0
certs=`${httpd} -t -DDUMP_CERTS 2>/dev/null`
RETVAL=$?
test $RETVAL -eq 0 || exit 0
for c in $certs; do
# Check whether a warning message is needed, then issue one if so.
/usr/bin/certwatch -q "$c" &&
/usr/bin/certwatch "$c" | /usr/sbin/sendmail -oem -oi -t 2>/dev/null
done

91
certwatch.xml Normal file
View File

@ -0,0 +1,91 @@
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry>
<refentryinfo>
<productname>crypto-utils</productname>
<date>September 2004</date>
</refentryinfo>
<refmeta>
<refentrytitle>certwatch</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>certwatch</refname>
<refpurpose>generate SSL certificate expiry warnings</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>certwatch</command>
<arg choice="opt"><option>-q</option></arg>
<arg choice="plain"><replaceable>filename</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>The <command>certwatch</command> program is used to issue
warning mail when an SSL certificate is about to expire.</para>
<para>The program has two modes of operation: normal mode and
quiet mode. In normal mode, the certificate given by the
<replaceable>filename</replaceable> argument is examined, and a
warning email is issued to standard output if the certificate is
outside its validity period, or approaching expiry. If the
certificate cannot be found, or any errors occur whilst parsing
the certificate, the certificate is ignored and no output is
produced.</para>
<para>In quiet mode (when the <literal>-q</literal> argument is
given), no output is ever produced.</para>
</refsect1>
<refsect1>
<title>Diagnostics</title>
<para>In both modes of operation, the exit code indicates the
state of the certificate:</para>
<variablelist>
<varlistentry>
<term><emphasis>0</emphasis></term>
<listitem><simpara>The certificate is outside its validity
period, or approaching expiry</simpara></listitem>
</varlistentry>
<varlistentry>
<term><emphasis>1</emphasis></term>
<listitem><simpara>The certificate is inside its validity
period, or could not be parsed</simpara></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para>The <command>certwatch</command> program is run daily by
<command>crond</command> from the file
<filename>/etc/cron.daily/certwatch</filename> to warn about the
imminent expiry of SSL certificates configured for use in the
Apache HTTP server. This warning can be disabled by adding the
line: <literal>NOCERTWATCH=yes</literal> to the file
<filename>/etc/sysconfig/httpd</filename>.</para>
</refsect1>
<refsect1>
<title>Files</title>
<para><filename>/etc/cron.daily/certwatch</filename></para>
</refsect1>
</refentry>

View File

@ -3,14 +3,17 @@
Summary: SSL certificate and key management utilities
Name: crypto-utils
Version: 2.0
Release: 6
Version: 2.1
Release: 1
Source: crypto-rand-%{crver}.tar.gz
Source1: genkey.pl
Source2: certwatch.c
Source3: certwatch.cron
Source4: certwatch.xml
Group: Applications/System
License: Various
BuildRoot: %{_tmppath}/%{name}-%{version}-root
BuildPreReq: openssl-devel, perl
BuildRequires: openssl-devel, perl, pkgconfig
Requires: newt-perl, openssl
Requires: %(eval `perl -V:version`; echo "perl(:MODULE_COMPAT_$version)")
Obsoletes: crypto-rand
@ -23,9 +26,13 @@ SSL certificates and keys.
%setup -q -n crypto-rand-%{crver}
%build
%configure --with-newt=%{_prefix} CFLAGS="-fPIC"
%configure --with-newt=%{_prefix} CFLAGS="-fPIC $RPM_OPT_FLAGS"
make
cc $RPM_OPT_FLAGS -Wall -Werror -I/usr/include/openssl -o certwatch \
$RPM_SOURCE_DIR/certwatch.c -lcrypto
xmlto man $RPM_SOURCE_DIR/certwatch.xml
pushd Makerand
perl -pi -e "s/Stronghold/Crypt/g" *
CFLAGS="$RPM_OPT_FLAGS" perl Makefile.PL PREFIX=$RPM_BUILD_ROOT/usr INSTALLDIRS=vendor
@ -33,6 +40,7 @@ make
popd
%install
rm -rf $RPM_BUILD_ROOT
pushd Makerand
make install
@ -51,12 +59,22 @@ find $RPM_BUILD_ROOT/usr -type f -print |
grep -v "\.packlist" > filelist
if [ ! -s filelist ] ; then
echo "ERROR: EMPTY FILE LIST"
exit -1
exit 1
fi
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/cron.daily \
$RPM_BUILD_ROOT%{_mandir}/man1 \
$RPM_BUILD_ROOT%{_bindir}
# install keyrand
%{__mkdir_p} $RPM_BUILD_ROOT%{_bindir}
install -m 755 keyrand/keyrand $RPM_BUILD_ROOT%{_bindir}/keyrand
install -c -m 755 keyrand/keyrand $RPM_BUILD_ROOT%{_bindir}/keyrand
# install certwatch
install -c -m 755 certwatch $RPM_BUILD_ROOT%{_bindir}/certwatch
install -c -m 755 $RPM_SOURCE_DIR/certwatch.cron \
$RPM_BUILD_ROOT%{_sysconfdir}/cron.daily/certwatch
install -c -m 644 certwatch.1 \
$RPM_BUILD_ROOT%{_mandir}/man1/certwatch.1
# install genkey
sed -e "s|^\$bindir.*$|\$bindir = \"/usr/bin\";|" \
@ -73,8 +91,14 @@ sed -e "s|^\$bindir.*$|\$bindir = \"/usr/bin\";|" \
%files -f filelist
%defattr(0644,root,root,0755)
%attr(0755,root,root) %{_bindir}/*
%{_sysconfdir}/cron.daily/certwatch
%{_mandir}/man1/certwatch.1*
%changelog
* Fri Sep 10 2004 Joe Orton <jorton@redhat.com> 2.1-1
- add /usr/bin/certwatch
- support --days argument to genkey (#131045)
* Tue Aug 17 2004 Joe Orton <jorton@redhat.com> 2.0-6
- add perl MODULE_COMPAT requirement

View File

@ -25,6 +25,7 @@
# 200305 Hide passwords entered for private key
# 200308 Adapted for Taroon
# 200308 Fix warnings in UTF-8 locale
# 200409 Added --days support
#
#
$bindir = "%INSTDIR%/bin";
@ -64,6 +65,7 @@ Usage: genkey [options] servername
--test Test mode, skip random data creation, overwrite existing key
--genreq Just generate a CSR from an existing key
--makeca Generate a private CA key instead
--days Days until expiry of self-signed certificate (default 30)
EOH
exit 1;
}
@ -109,8 +111,10 @@ sub RunForm
my $test_mode = '';
my $genreq_mode = '';
my $ca_mode = '';
my $cert_days = 30;
GetOptions('test|t' => \$test_mode,
'genreq' => \$genreq_mode,
'days=i' => \$cert_days,
'makeca' => \$ca_mode) or usage();
usage() unless @ARGV != 0;
$skip_random = $test_mode;
@ -877,7 +881,7 @@ sub genReqWindow
if (!$genreq_mode) {
if (!-f $certfile) {
makeCert($keyfile,$certfile,$cadetails,"-days 30 -x509");
makeCert($keyfile,$certfile,$cadetails,"-days $cert_days -x509");
}
}
@ -981,7 +985,7 @@ sub genCertWindow
my $ret = getCertDetails($servername,$msg, 0);
return $ret unless ($ret eq "Next");
makeCert($keyfile,$certfile,$cadetails,"-days 30 -x509");
makeCert($keyfile,$certfile,$cadetails,"-days $cert_days -x509");
return "Next";
}