crypto-utils/certwatch.c
jorton 51f1bc07ed - add /usr/bin/certwatch
- support --days argument to genkey (#131045)
2004-09-10 14:16:06 +00:00

183 lines
5.7 KiB
C

/*
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;
}