parent
07af1dc964
commit
51f1bc07ed
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue