51f1bc07ed
- support --days argument to genkey (#131045)
183 lines
5.7 KiB
C
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;
|
|
}
|