Use nss library for cryptography (#346731)
This commit is contained in:
parent
40da9fa7c0
commit
f65b51384d
43
Makefile
43
Makefile
@ -20,13 +20,19 @@ endif
|
||||
|
||||
include $(MAKEFILE_COMMON)
|
||||
|
||||
certwatch: certwatch.c
|
||||
gcc -Wall -Werror -O2 -g $< -o $@ -lcrypto
|
||||
certwatch: certwatch.c pemutil.c
|
||||
gcc -Wall -Werror -O2 -g $^ -o $@ \
|
||||
-lnspr4 -lnss3 -I/usr/include/nspr4 -I/usr/include/nss3
|
||||
|
||||
test-certwatch: certwatch
|
||||
./certwatch
|
||||
|
||||
genkey: genkey.pl Makefile
|
||||
keyutil: keyutil.c keyutil.h certext.c secutil.c secutil.h secerror.c
|
||||
gcc -Wall -Werror -O2 -g $^ -o $@ \
|
||||
-lnspr4 -lnss3 -I/usr/include/nspr4 -I/usr/include/nss3
|
||||
chmod 755 $@
|
||||
|
||||
genkey: genkey.pl keyutil Makefile
|
||||
sed -e "s|^\$$bindir.*$$|\$$bindir = \"/usr/bin\";|" \
|
||||
-e "s|^\$$ssltop.*$$|\$$ssltop = \"$(PWD)\";|" \
|
||||
-e "s|^\$$sslconf.*$$|\$$sslconf = \"/etc/pki/tls/openssl.cnf\";|" \
|
||||
@ -40,6 +46,37 @@ test-genkey: genkey
|
||||
mkdir -p certs private
|
||||
./genkey --test `hostname`
|
||||
|
||||
test-genkey-modnss: genkey
|
||||
mkdir -p certs private
|
||||
./genkey --test --nss test.`hostname`
|
||||
|
||||
#########################################################################
|
||||
# The following test targets run genkey with debug tracing on, which
|
||||
# creates temporary files, and the nss utilities with gdb. Use the
|
||||
# cleanup-tests to help clean up after yourself. The -modnss targets may
|
||||
# need to be run as super user in order to access the database.
|
||||
#########################################################################
|
||||
|
||||
test-genreq-modssl: genkey
|
||||
perl ./genkey --genreq -d test.`hostname`
|
||||
|
||||
test-makecert-modssl: genkey
|
||||
perl ./genkey --makeca -d test.`hostname`
|
||||
|
||||
test-genreq-modnss: genkey
|
||||
perl ./genkey --genreq -d -n test.`hostname`
|
||||
|
||||
test-makecert-modnss: genkey
|
||||
perl ./genkey --makeca -d -n test.`hostname`
|
||||
|
||||
prepare-tests:
|
||||
mkdir -p certs private
|
||||
|
||||
cleanup-tests: certs private
|
||||
rm -f -r certs private
|
||||
|
||||
#########################################################################
|
||||
|
||||
date.xml:
|
||||
date +"%e %B %Y" | tr -d '\n' > $@
|
||||
|
||||
|
308
certwatch.c
308
certwatch.c
@ -27,13 +27,63 @@
|
||||
|
||||
*/
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Netscape security libraries.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/* Certificate expiry warning generation code, based on code from
|
||||
* Stronghold. Joe Orton <jorton@redhat.com> */
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
/* Replaced usage of OpenSSL with NSS.
|
||||
* Elio Maldonado <emaldona@redhat.com> */
|
||||
|
||||
#include <nspr.h>
|
||||
#include <nss.h>
|
||||
#include <cert.h>
|
||||
#include <certt.h>
|
||||
#include <prlong.h>
|
||||
#include <prtime.h>
|
||||
#include <pk11func.h>
|
||||
#include <assert.h>
|
||||
#include <secmod.h>
|
||||
#include <base64.h>
|
||||
#include <seccomon.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -41,55 +91,109 @@
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
|
||||
#define TIME_BUF_SIZE 100
|
||||
|
||||
/* Return a certificate structure from a pem-encoded cert in a file;
|
||||
* or NULL on failure. Semantics similar to the OpenSSL call
|
||||
* PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
*/
|
||||
extern CERTCertificate *
|
||||
PEMUTIL_PEM_read_X509(const char *filename);
|
||||
|
||||
/* size big enough for formatting time buffer */
|
||||
#define TIME_SIZE 30
|
||||
|
||||
static int warn_period = 30;
|
||||
static char *warn_address = "root";
|
||||
|
||||
/* Turn an ASN.1 UTCTIME object into a time_t. */
|
||||
static time_t decode_utctime(const ASN1_UTCTIME *utc)
|
||||
/* Format a PRTime value into a buffer with format "%a %b %d %H:%M:%S %Y"
|
||||
* and return the buffer. The buffer returned must the freed with PORT_Free.
|
||||
* Semantics are similar to asctime.
|
||||
*/
|
||||
char * AsciiTime(PRTime time)
|
||||
{
|
||||
PRExplodedTime printable;
|
||||
char *timebuf;
|
||||
|
||||
PR_ExplodeTime(time, PR_GMTParameters, &printable);
|
||||
timebuf = PORT_Alloc(TIME_BUF_SIZE);
|
||||
(void) PR_FormatTime(timebuf, TIME_BUF_SIZE, "%a %b %d %H:%M:%S %Y", &printable);
|
||||
if (strlen(timebuf) < TIME_BUF_SIZE) timebuf[strlen(timebuf)] = '\0';
|
||||
return (timebuf);
|
||||
}
|
||||
|
||||
/* Uses the password passed in the -f(pwfile) argument of the command line.
|
||||
* After use once, null it out otherwise PKCS11 calls us forever.?
|
||||
*
|
||||
* Code based on SECU_GetModulePassword from the Mozilla NSS secutils
|
||||
* imternal librart.
|
||||
*/
|
||||
static char *GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
|
||||
{
|
||||
struct tm tm = {0};
|
||||
int i = utc->length;
|
||||
int i;
|
||||
unsigned char phrase[200];
|
||||
PRFileDesc *fd;
|
||||
PRInt32 nb;
|
||||
char *pwFile = arg;
|
||||
|
||||
if (i < 10)
|
||||
return -1;
|
||||
for (i = 0; i < 10; i++)
|
||||
if ((utc->data[i] > '9') || (utc->data[i] < '0'))
|
||||
return -1;
|
||||
if (!pwFile) return 0;
|
||||
if (retry) return 0; /* no good retrying - file contents will be the same */
|
||||
if (!(fd = PR_Open(pwFile, PR_RDONLY, 0))) return 0;
|
||||
|
||||
tm.tm_year = (utc->data[0]-'0') * 10 + (utc->data[1]-'0');
|
||||
nb = PR_Read(fd, phrase, sizeof(phrase));
|
||||
PR_Close(fd);
|
||||
|
||||
/* handle the Windows EOL case */
|
||||
i = 0;
|
||||
while (phrase[i] != '\r' && phrase[i] != '\n' && i < nb) i++;
|
||||
phrase[i] = '\0';
|
||||
if (nb == 0) return NULL;
|
||||
|
||||
/* Deal with Year 2000 like eay did */
|
||||
if (tm.tm_year < 70)
|
||||
tm.tm_year += 100;
|
||||
return (char*) PORT_Strdup((char*)phrase);
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
return mktime(&tm) - timezone;
|
||||
/* Format a PRTime value into a buffer with format "%a %b %d %H:%M:%S %Y";
|
||||
* semantics are those of ctime_r(). */
|
||||
char *pr_ctime(PRTime time, char *buf, int size)
|
||||
{
|
||||
PRUint32 bytesCopied;
|
||||
PRExplodedTime et;
|
||||
PR_ExplodeTime(time, PR_GMTParameters, &et);
|
||||
bytesCopied = PR_FormatTime(buf, size, "%a %b %d %H:%M:%S %Y", &et);
|
||||
if (!bytesCopied) return NULL;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
SECCertTimeValidity validity,
|
||||
PRTime start, PRTime end, PRTime now, int quiet)
|
||||
{
|
||||
int renew = 1, days = (end - now) / (3600 * 24); /* days till expiry */
|
||||
/* Note that filename can be the cert nickname. */
|
||||
int renew = 1;
|
||||
char subj[50];
|
||||
|
||||
if (start > now) {
|
||||
PRTime prtimeDiff;
|
||||
|
||||
LL_SUB(prtimeDiff, end, start);
|
||||
|
||||
if ( LL_CMP(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 < warn_period) {
|
||||
sprintf(subj, "will expire in %d days", days);
|
||||
} else if (LL_EQ(now, end)) {
|
||||
strcpy(subj, "will expire today");
|
||||
} else if (LL_EQ(prtimeDiff, 1)) {
|
||||
sprintf(subj, "will expire tomorrow");
|
||||
} else if (LL_CMP(prtimeDiff, <, warn_period)) {
|
||||
sprintf(subj, "will expire on %s", AsciiTime(end));
|
||||
{
|
||||
int days; /* days till expiry */
|
||||
LL_L2I(days, prtimeDiff);
|
||||
days = (days) / (3600 * 24);
|
||||
assert(0 < days);
|
||||
assert(days < warn_period);
|
||||
sprintf(subj, "will expire in %d days", days);
|
||||
}
|
||||
} else {
|
||||
return 0; /* nothing to warn about. */
|
||||
}
|
||||
@ -104,7 +208,7 @@ static int warning(FILE *out, const char *filename, const char *hostname,
|
||||
" ################# SSL Certificate Warning ################\n\n");
|
||||
|
||||
fprintf(out,
|
||||
" Certificate for hostname '%s', in file:\n"
|
||||
" Certificate for hostname '%s', in file (or by nickname):\n"
|
||||
" %s\n\n",
|
||||
hostname, filename);
|
||||
|
||||
@ -115,9 +219,10 @@ static int warning(FILE *out, const char *filename, const char *hostname,
|
||||
" 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';
|
||||
char until[TIME_SIZE];
|
||||
char *result = pr_ctime(start, until, TIME_SIZE);
|
||||
assert(result == until);
|
||||
if (strlen(until) < sizeof(until)) until[strlen(until)] = '\0';
|
||||
fprintf(out,
|
||||
" The certificate is not valid until %s.\n\n"
|
||||
" Browsers will not be able to correctly connect to this\n"
|
||||
@ -133,56 +238,73 @@ static int warning(FILE *out, const char *filename, const char *hostname,
|
||||
}
|
||||
|
||||
/* Extract the common name of 'cert' into 'buf'. */
|
||||
static int get_common_name(X509 *cert, char *buf, size_t bufsiz)
|
||||
static int get_common_name(CERTCertificate *cert, char *buf, size_t bufsiz)
|
||||
{
|
||||
X509_NAME *name = X509_get_subject_name(cert);
|
||||
|
||||
/* FIXME --- truncating names with spaces */
|
||||
size_t namelen;
|
||||
char *name = CERT_GetCommonName(&cert->subject);
|
||||
|
||||
if (!name) return -1;
|
||||
|
||||
return X509_NAME_get_text_by_NID(name, NID_commonName, buf, bufsiz) == -1;
|
||||
namelen = strlen(name);
|
||||
if (bufsiz < namelen+1) return -1;
|
||||
|
||||
strncpy(buf, name, namelen);
|
||||
buf[namelen] = '\0';
|
||||
PORT_Free(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether the certificate in filename 'filename' has expired;
|
||||
/* Check whether the certificate in filename 'name' 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)
|
||||
* occurred.
|
||||
*
|
||||
* When byNickname is 1 then 'name' is a nickname to search
|
||||
* for in the database otherwise it's the certificate file.
|
||||
*/
|
||||
static int check_cert(const char *name, int byNickname, int quiet)
|
||||
{
|
||||
X509 *cert;
|
||||
FILE *fp;
|
||||
ASN1_UTCTIME *notAfter, *notBefore;
|
||||
time_t begin, end, now;
|
||||
CERTCertificate *cert;
|
||||
SECCertTimeValidity validity;
|
||||
PRTime notBefore, notAfter;
|
||||
char cname[128];
|
||||
|
||||
int doWarning = 0;
|
||||
|
||||
/* parse the cert */
|
||||
if ((fp = fopen(filename, "r")) == NULL) return -1;
|
||||
cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
fclose(fp);
|
||||
cert = byNickname
|
||||
? CERT_FindCertByNickname(CERT_GetDefaultCertDB(), (char *)name)
|
||||
: PEMUTIL_PEM_read_X509(name);
|
||||
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;
|
||||
validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE);
|
||||
if (validity == secCertTimeUndetermined) goto cleanup;
|
||||
|
||||
/* get times out of the cert */
|
||||
if (CERT_GetCertTimes(cert, ¬Before, ¬After)
|
||||
!= SECSuccess) goto cleanup;
|
||||
|
||||
/* find the subject's commonName attribute */
|
||||
if (get_common_name(cert, cname, sizeof cname))
|
||||
return -1;
|
||||
goto cleanup;
|
||||
|
||||
X509_free(cert);
|
||||
|
||||
/* don't warn about the automatically generate certificate */
|
||||
/* don't warn about the automatically generated certificate */
|
||||
if (strcmp(cname, "localhost") == 0 ||
|
||||
strcmp(cname, "localhost.localdomain") == 0)
|
||||
return -1;
|
||||
goto cleanup;
|
||||
|
||||
return warning(stdout, filename, cname, begin, end, now, quiet);
|
||||
doWarning = 1; /* ok so far, may do the warning */
|
||||
|
||||
cleanup:
|
||||
if (cert) CERT_DestroyCertificate(cert);
|
||||
if (!doWarning) return -1;
|
||||
|
||||
return warning(stdout, name, cname, validity,
|
||||
notBefore, notAfter, PR_Now(), quiet);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -192,14 +314,25 @@ int main(int argc, char **argv)
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "period", required_argument, NULL, 'p' },
|
||||
{ "address", required_argument, NULL, 'a' },
|
||||
{ "configdir", required_argument, NULL, 'd' },
|
||||
{ "passwordfile", required_argument, NULL, 'w' },
|
||||
{ "certdbprefix", required_argument, NULL, 'w' },
|
||||
{ "keydbprexix", required_argument, NULL, 'w' },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
char *certDBPrefix = "";
|
||||
char *keyDBPrefix = "";
|
||||
|
||||
char *configdir = NULL; /* contains the cert database */
|
||||
char *passwordfile = NULL; /* module password file */
|
||||
int byNickname = 0; /* whether to search by nickname */
|
||||
|
||||
/* The 'timezone' global is needed to adjust local times from
|
||||
* mktime() back to UTC: */
|
||||
tzset();
|
||||
|
||||
while ((optc = getopt_long(argc, argv, "qp:a:", options, NULL)) != -1) {
|
||||
while ((optc = getopt_long(argc, argv, "qp:a:d:w:", options, NULL)) != -1) {
|
||||
switch (optc) {
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
@ -210,11 +343,50 @@ int main(int argc, char **argv)
|
||||
case 'a':
|
||||
warn_address = strdup(optarg);
|
||||
break;
|
||||
case 'd':
|
||||
configdir = strdup(optarg);
|
||||
byNickname = 1;
|
||||
break;
|
||||
case 'w':
|
||||
passwordfile = strdup(optarg);
|
||||
break;
|
||||
case 'c':
|
||||
certDBPrefix = strdup(optarg);
|
||||
break;
|
||||
case 'k':
|
||||
keyDBPrefix = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* NSS initialization */
|
||||
|
||||
return check_cert(argv[optind], quiet) == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
if (byNickname) {
|
||||
/* cert in database */
|
||||
if (NSS_Initialize(configdir, certDBPrefix, keyDBPrefix,
|
||||
SECMOD_DB, NSS_INIT_READONLY) != SECSuccess) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* in case module requires a password */
|
||||
if (passwordfile) {
|
||||
PK11_SetPasswordFunc(GetModulePassword);
|
||||
}
|
||||
} else {
|
||||
/* cert in a pem file */
|
||||
char *certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */
|
||||
if (!certDir) {
|
||||
certDir = "/etc/pki/nssdb";
|
||||
}
|
||||
if (NSS_Initialize(certDir, certDBPrefix, keyDBPrefix,
|
||||
SECMOD_DB, NSS_INIT_READONLY) != SECSuccess) {
|
||||
printf("NSS_Init(\"%s\") failed\n", certDir);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* When byNickname is 1 argv[optind] is a nickname otherwise a filename. */
|
||||
return check_cert(argv[optind], byNickname, quiet) == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
@ -6,6 +6,47 @@
|
||||
# CERTWATCH_OPTS variable; see the man page for details.
|
||||
#
|
||||
|
||||
# For certificates in pem files
|
||||
watch_files_certs()
|
||||
{
|
||||
test -x /etc/httpd/modules/mod_ssl.so || return 0
|
||||
test -r /etc/httpd/conf/httpd.conf || return 0
|
||||
|
||||
set -o pipefail # pick up exit code of httpd not sort
|
||||
|
||||
certs=`${httpd} ${OPTIONS} -t -DDUMP_CERTS 2>/dev/null | /bin/sort -u`
|
||||
RETVAL=$?
|
||||
test $RETVAL -eq 0 || return
|
||||
|
||||
for c in $certs; do
|
||||
# Check whether a warning message is needed, then issue one if so.
|
||||
/usr/bin/certwatch $CERTWATCH_OPTS -q "$c" &&
|
||||
/usr/bin/certwatch $CERTWATCH_OPTS "$c" | /usr/sbin/sendmail -oem -oi -t 2>/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
# For certificates in the database
|
||||
watch_database_certs()
|
||||
{
|
||||
test -x /usr/bin/certutil || return 0
|
||||
test -x /usr/lib/httpd/modules/libmodnss.so || return 0
|
||||
test -r /etc/httpd/conf.d/nss.conf || return 0
|
||||
|
||||
# find path to mod_nss' database
|
||||
database=`/usr/bin/gawk '/^NSSCertificateDatabase/ { print $2 }' /etc/httpd/conf.d/nss.conf`
|
||||
|
||||
set -o pipefail # pick up exit code of certutil not gawk
|
||||
nicknames=`certutil -L -d $database | /usr/bin/gawk '{ print $1 }'`
|
||||
RETVAL=$?
|
||||
test $RETVAL -eq 0 || return 0
|
||||
|
||||
for n in $nicknames; do
|
||||
# Check whether a warning message is needed, then issue one if so.
|
||||
/usr/bin/certwatch $CERTWATCH_OPTS -q -d "$database" "$n" &&
|
||||
/usr/bin/certwatch $CERTWATCH_OPTS -d "$database" "$n" | /usr/sbin/sendmail -oem -oi -t 2>/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
[ -r /etc/sysconfig/httpd ] && . /etc/sysconfig/httpd
|
||||
|
||||
# Use configured httpd binary
|
||||
@ -15,19 +56,8 @@ httpd=${HTTPD-/usr/sbin/httpd}
|
||||
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
|
||||
test -x /bin/sort || exit 0
|
||||
|
||||
set -o pipefail # pick up exit code of httpd not sort
|
||||
|
||||
certs=`${httpd} ${OPTIONS} -t -DDUMP_CERTS 2>/dev/null | /bin/sort -u`
|
||||
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 $CERTWATCH_OPTS -q "$c" &&
|
||||
/usr/bin/certwatch $CERTWATCH_OPTS "$c" | /usr/sbin/sendmail -oem -oi -t 2>/dev/null
|
||||
done
|
||||
watch_files_certs
|
||||
watch_database_certs
|
||||
|
@ -49,6 +49,9 @@
|
||||
the certificate, the certificate is ignored and no output is
|
||||
produced. In quiet mode, no output is given, but the exit status
|
||||
can still be used.</para>
|
||||
|
||||
<para>The certificate can be specified by its nickname or by a
|
||||
path to the containing file.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
@ -86,6 +89,14 @@
|
||||
default is <literal>root</literal>.</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--directory <replaceable>cert-directory</replaceable></option>,
|
||||
<option>-d <replaceable>cert-directory</replaceable></option></term>
|
||||
|
||||
<listitem><simpara>Specify the database directory containing the certificate
|
||||
and key database files. The default is yet to be determined.</simpara></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
Summary: SSL certificate and key management utilities
|
||||
Name: crypto-utils
|
||||
Version: 2.3
|
||||
Release: 10
|
||||
Version: 2.4
|
||||
Release: 1
|
||||
Source: crypto-rand-%{crver}.tar.gz
|
||||
Source1: genkey.pl
|
||||
Source2: certwatch.c
|
||||
@ -14,12 +14,22 @@ Source5: genkey.xml
|
||||
Source6: keyrand.c
|
||||
Source7: COPYING
|
||||
Source8: keyrand.xml
|
||||
Source9: pemutil.c
|
||||
Source10: keyutil.c
|
||||
Source11: certext.c
|
||||
Source12: secutil.c
|
||||
Source13: secerror.c
|
||||
Source14: keyutil.h
|
||||
Source15: secutil.h
|
||||
Source16: NSPRerrs.h
|
||||
Source17: SECErrs.h
|
||||
Source18: copying
|
||||
Group: Applications/System
|
||||
License: MIT and GPLv2+
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
||||
BuildRequires: openssl-devel, pkgconfig, newt-devel, xmlto
|
||||
BuildRequires: nss-devel, pkgconfig, newt-devel, xmlto
|
||||
BuildRequires: perl-devel, perl(Newt), perl(ExtUtils::MakeMaker)
|
||||
Requires: perl(Newt), openssl >= 0.9.7f-4
|
||||
Requires: perl(Newt), nss >= 3.11.99.5-2fc-9
|
||||
Requires: %(eval `perl -V:version`; echo "perl(:MODULE_COMPAT_$version)")
|
||||
Obsoletes: crypto-rand
|
||||
|
||||
@ -34,8 +44,16 @@ SSL certificates and keys.
|
||||
%configure --with-newt=%{_prefix} CFLAGS="$CFLAGS -fPIC"
|
||||
make -C librand
|
||||
|
||||
cc $RPM_OPT_FLAGS -Wall -Werror -I/usr/include/openssl \
|
||||
$RPM_SOURCE_DIR/certwatch.c -o certwatch -lcrypto
|
||||
cc $RPM_OPT_FLAGS -Wall -Werror -I/usr/include/nspr4 -I/usr/include/nss3 \
|
||||
$RPM_SOURCE_DIR/certwatch.c $RPM_SOURCE_DIR/pemutil.c \
|
||||
-o certwatch -lnspr4 -lnss3
|
||||
|
||||
cc $RPM_OPT_FLAGS -Wall -Werror -I/usr/include/nspr4 -I/usr/include/nss3 \
|
||||
$RPM_SOURCE_DIR/keyutil.c \
|
||||
$RPM_SOURCE_DIR/certext.c \
|
||||
$RPM_SOURCE_DIR/secutil.c \
|
||||
$RPM_SOURCE_DIR/secerror.c \
|
||||
-o keyutil -lnspr4 -lnss3
|
||||
|
||||
cc $RPM_OPT_FLAGS -Wall -Werror \
|
||||
$RPM_SOURCE_DIR/keyrand.c -o keyrand -lnewt
|
||||
@ -85,6 +103,9 @@ for f in certwatch genkey keyrand; do
|
||||
install -c -m 644 ${f}.1 $RPM_BUILD_ROOT%{_mandir}/man1/${f}.1
|
||||
done
|
||||
|
||||
# install keyutil
|
||||
install -c -m 755 keyutil $RPM_BUILD_ROOT%{_bindir}/keyutil
|
||||
|
||||
# install genkey
|
||||
sed -e "s|^\$bindir.*$|\$bindir = \"%{_bindir}\";|" \
|
||||
-e "s|^\$ssltop.*$|\$ssltop = \"/etc/pki/tls\";|" \
|
||||
|
521
genkey.pl
521
genkey.pl
@ -32,11 +32,12 @@
|
||||
# 200308 Adapted for Taroon
|
||||
# 200308 Fix warnings in UTF-8 locale
|
||||
# 200409 Added --days support
|
||||
# 200804 Use NSS library for cryptography [Bug 346731]
|
||||
#
|
||||
#
|
||||
$bindir = "%INSTDIR%/bin";
|
||||
$ssltop = "%INSTDIR%/conf/ssl";
|
||||
$sslconf = "%INSTDIR%/conf/ssl/openssl.conf";
|
||||
$nssconf = "/etc/httpd/conf.d/nss.conf";
|
||||
$cadir = "$ssltop/CA";
|
||||
|
||||
use Crypt::Makerand;
|
||||
@ -72,6 +73,8 @@ Usage: genkey [options] servername
|
||||
--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)
|
||||
--nss Use the mod_nss database for keys and certificates
|
||||
--debug Enable debug logs to files
|
||||
EOH
|
||||
exit 1;
|
||||
}
|
||||
@ -118,15 +121,22 @@ my $test_mode = '';
|
||||
my $genreq_mode = '';
|
||||
my $ca_mode = '';
|
||||
my $cert_days = 30;
|
||||
my $nss='';
|
||||
my $debug='';
|
||||
my $modNssDbDir = '';
|
||||
my $nickname = '';
|
||||
GetOptions('test|t' => \$test_mode,
|
||||
'genreq' => \$genreq_mode,
|
||||
'days=i' => \$cert_days,
|
||||
'days=i' => \$cert_days,
|
||||
'nss|n' => \$nss,
|
||||
'debug|d'=> \$debug,
|
||||
'makeca' => \$ca_mode) or usage();
|
||||
usage() unless @ARGV != 0;
|
||||
$skip_random = $test_mode;
|
||||
$overwrite_key = $test_mode;
|
||||
$servername = $ARGV[0];
|
||||
$randfile = $ssltop."/.rand.".$$;
|
||||
$tmpPasswordFile = ''; # none has been created yet
|
||||
$keyfile = $ssltop."/private/".$servername.".key";
|
||||
if ($ca_mode) {
|
||||
$keyfile = $cadir."/private/".$servername;
|
||||
@ -136,7 +146,7 @@ if ($ca_mode) {
|
||||
my $bits = 0;
|
||||
my $myca = "Other";
|
||||
my $useca = 0;
|
||||
my $cadetails;
|
||||
my $subject;
|
||||
#
|
||||
|
||||
Newt::Init();
|
||||
@ -160,11 +170,19 @@ if (!$genreq_mode && -f $keyfile && !$overwrite_key) {
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if ($genreq_mode && !(-f $keyfile)) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"You do not have a key file for this host");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
# For mod_nss we need the database and nickname set
|
||||
if ($nss) {
|
||||
# the configuration file is required
|
||||
if (!nssconfigFound()) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Could not find mod_nss's nss.conf file".
|
||||
"for this host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$modNssDbDir = getModNSSDatabase();
|
||||
$nickname = getNickname();
|
||||
}
|
||||
|
||||
######################################################################
|
||||
@ -183,32 +201,38 @@ if ($genreq_mode && !(-f $keyfile)) {
|
||||
my @windows;
|
||||
if ($genreq_mode) {
|
||||
$useca = 1;
|
||||
@windows = (genReqWindow,
|
||||
@windows = (
|
||||
getkeysizeWindow,
|
||||
customKeySizeWindow,
|
||||
getRandomDataWindow,
|
||||
keyPasswordWindow,
|
||||
genReqWindow,
|
||||
);
|
||||
$doingwhat="CSR generation";
|
||||
} elsif ($ca_mode) {
|
||||
@windows = (CAwelcomeWindow,
|
||||
getkeysizeWindow,
|
||||
customKeySizeWindow,
|
||||
getRandomDataWindow, ## leaves newt suspended
|
||||
generateKey,
|
||||
getRandomDataWindow,
|
||||
keyPasswordWindow,
|
||||
genCACertWindow,
|
||||
encryptKeyWindow,
|
||||
);
|
||||
$doingwhat="CA key generation";
|
||||
} else {
|
||||
@windows = (welcomeWindow,
|
||||
getkeysizeWindow,
|
||||
customKeySizeWindow,
|
||||
getRandomDataWindow, ## leaves newt suspended
|
||||
generateKey,
|
||||
getRandomDataWindow,
|
||||
wantCAWindow,
|
||||
whichCAWindow,
|
||||
keyPasswordWindow,
|
||||
genReqWindow,
|
||||
genCertWindow,
|
||||
encryptKeyWindow,
|
||||
### @EXTRA@ ### Leave this comment here.
|
||||
);
|
||||
$doingwhat="key generation";
|
||||
genReqWindow,
|
||||
genCertWindow,
|
||||
### @EXTRA@ ### Leave this comment here.
|
||||
);
|
||||
$doingwhat="testing request and cert generation";
|
||||
}
|
||||
|
||||
my $screen = 0;
|
||||
@ -247,10 +271,15 @@ while ($screen <= $#windows) {
|
||||
}
|
||||
|
||||
# Exit
|
||||
clearSensitiveData();
|
||||
Newt::Finished();
|
||||
exit 1 if ($result eq "Cancel");
|
||||
exit 0;
|
||||
|
||||
#
|
||||
# end main
|
||||
#
|
||||
|
||||
######################################################################
|
||||
# Handy functions
|
||||
|
||||
@ -279,6 +308,58 @@ sub NextBackCancelButton {
|
||||
->Add(2, 0, $cancelb, Newt::NEWT_ANCHOR_LEFT(), 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
# Check that nss.conf exists
|
||||
sub nssconfigFound {
|
||||
# if it isn't in its usual place
|
||||
if (!$nssconf || !(-f $nssconf)) {
|
||||
# do an rpm query
|
||||
my $cmd = 'rpm -ql mod_nss';
|
||||
my $tmplist = "list";
|
||||
system("$cmd > $tmplist");
|
||||
$nssconf = `grep nss.conf $tmplist`;
|
||||
unlink($tmplist);
|
||||
}
|
||||
return ($nssconf && (-f $nssconf));
|
||||
}
|
||||
|
||||
# Returns the mod_nss database directory path.
|
||||
sub getModNSSDatabase {
|
||||
|
||||
# Extract the value from the mod_nss configuration file.
|
||||
my $cmd ='/usr/bin/gawk \'/^NSSCertificateDatabase/ { print $2 }\'' . " $nssconf";
|
||||
my $dbfile = "dbdirectory";
|
||||
system("$cmd > $dbfile");
|
||||
open(DIR, "<$dbfile");
|
||||
my $dbdir = <DIR>;
|
||||
unlink($dbfile);
|
||||
|
||||
return $dbdir;
|
||||
}
|
||||
|
||||
# Returns the rsa server name.
|
||||
sub getNickname {
|
||||
|
||||
# Extract the value from the mod_nss configuration file.
|
||||
my $cmd ='/usr/bin/gawk \'/^NSSNickname/ { print $2 }\'' . " $nssconf";
|
||||
my $nicknamefile = "nickname";
|
||||
system("$cmd > $nicknamefile");
|
||||
open(NICK, "<$nicknamefile");
|
||||
my $nickname = <NICK>;
|
||||
unlink($nicknamefile);
|
||||
|
||||
return "test-".$nickname;
|
||||
}
|
||||
|
||||
# Erases and deletes the password file
|
||||
sub clearSensitiveData {
|
||||
if (-f $tmpPasswordFile) {
|
||||
open(DOOMED,$tmpPasswordFile);
|
||||
truncate(DOOMED,0);
|
||||
close(DOOMED);
|
||||
unlink($tmpPasswordFile);
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# The window functions
|
||||
|
||||
@ -436,6 +517,7 @@ EOT
|
||||
sub welcomeWindow()
|
||||
{
|
||||
my $name = $servername;
|
||||
my $where = $nss ? $modNssDbDir : "$ssltop/private/$name.key";
|
||||
my $message = <<EOT;
|
||||
You are now generating a new keypair which will be used to encrypt all
|
||||
SSL traffic to the server named $name.
|
||||
@ -443,12 +525,12 @@ Optionally you can also create a certificate request and send it to a
|
||||
certificate authority (CA) for signing.
|
||||
|
||||
The key will be stored in
|
||||
$ssltop/private/$name.key
|
||||
$where
|
||||
The certificate stored in
|
||||
$ssltop/certs/$name.cert
|
||||
|
||||
If the key generation fails, move the file
|
||||
$ssltop/private/$name.key
|
||||
$where
|
||||
to a backup location and try again.
|
||||
EOT
|
||||
|
||||
@ -470,15 +552,16 @@ EOT
|
||||
sub CAwelcomeWindow()
|
||||
{
|
||||
my $name = $servername;
|
||||
my $where = $nss ? $modNssDbDir : "$cadir/private/$name";
|
||||
my $message = <<EOT;
|
||||
You are now generating a new keypair which will be used for your
|
||||
private CA
|
||||
|
||||
The key will be stored in
|
||||
$cadir/private/$name
|
||||
$where
|
||||
|
||||
If the key generation fails, move the file
|
||||
$cadir/private/$name
|
||||
$where
|
||||
to a backup location and try again.
|
||||
EOT
|
||||
|
||||
@ -522,7 +605,29 @@ sub wantCAWindow
|
||||
return "Next";
|
||||
}
|
||||
|
||||
sub encryptKeyWindow
|
||||
# Save the passphrase to a temporary file.
|
||||
sub savePassword
|
||||
{
|
||||
my ($passwd) = @_;
|
||||
|
||||
$tmpPasswordFile = ".passwordfile.".$$;
|
||||
|
||||
if (!open (SESAME, ">$tmpPasswordFile")) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Unable to save passphrase to $tmpPasswordFile".
|
||||
"\n\nPress return to continue");
|
||||
$tmpPasswordFile = ''; # mark it as never created
|
||||
return "Back";
|
||||
}
|
||||
print SESAME $passwd."\n\n";
|
||||
close(SESAME);
|
||||
# This file will be deleted on program exit.
|
||||
|
||||
return "Next";
|
||||
}
|
||||
|
||||
# Prompts for key encryption password
|
||||
sub keyPasswordWindow
|
||||
{
|
||||
my $message = <<EOT;
|
||||
At this stage you can set the passphrase on your private key. If you
|
||||
@ -530,10 +635,10 @@ set the passphrase you will have to enter it every time the server
|
||||
starts. The passphrase you use to encrypt your key must be the same
|
||||
for all the keys used by the same server installation.
|
||||
|
||||
If you do not encrypt your passphrase, if someone breaks into your
|
||||
If you do not encrypt your key, then if someone breaks into your
|
||||
server and grabs the file containing your key, they will be able to
|
||||
decrypt all communications to and from the server that were negotiated
|
||||
using that key. If your passphrase is encrypted it would be much more
|
||||
using that key. If your key is encrypted it would be much more
|
||||
work for someone to retrieve the private key.
|
||||
EOT
|
||||
$panel = Newt::Panel(1, 3, "Protecting your private key");
|
||||
@ -617,104 +722,221 @@ EOT
|
||||
|
||||
return $ret if ($ret eq "Back" or $ret eq "Cancel");
|
||||
|
||||
my $enckey = $keyfile . ".tmp";
|
||||
# Save it to a temporary file to supply to the nss utilities,
|
||||
# the file will be erased upon exit
|
||||
savePassword($pass1);
|
||||
|
||||
unlink($enckey);
|
||||
|
||||
if (!open (PIPE,
|
||||
"|$bindir/openssl rsa -des3 -in $keyfile -passout stdin ".
|
||||
"-out $enckey")) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Unable to set passphrase".
|
||||
"\n\nPress return to continue");
|
||||
return "Back";
|
||||
}
|
||||
print PIPE $pass1."\n";
|
||||
close(PIPE);
|
||||
|
||||
if (-f $enckey) {
|
||||
if (chmod(0400, $enckey) != 1
|
||||
|| !rename($enckey, $keyfile)) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Could not install private key file.\n".
|
||||
"$! - $enckey");
|
||||
unlink($enckey);
|
||||
return "Back";
|
||||
}
|
||||
} else {
|
||||
Newt:newtWinMessage("Error", "Close",
|
||||
"Unable to set passphrase\n\n".
|
||||
"Press return to continue");
|
||||
return "Back";
|
||||
}
|
||||
return "Next";
|
||||
}
|
||||
|
||||
sub genReqblah()
|
||||
{
|
||||
my ($name) = @_;
|
||||
my $message = <<EOT;
|
||||
Now we will create a self-signed certificate for use until the CA of your
|
||||
choice signs your certificate. You will have to use this cert until
|
||||
your CA responds with the actual signed certificate.
|
||||
EOT
|
||||
#
|
||||
# Bottleneck routine to call the nss utilies.
|
||||
# Calls are bracketed by newt suspend and resume
|
||||
# enabling user interaction from the nss utilities
|
||||
# and trace messages to the console.
|
||||
#
|
||||
sub nssUtilCmd {
|
||||
|
||||
my ($debug, $cmd, $args, $msg) = @_;
|
||||
|
||||
my $panel = Newt::Panel(1, 2, "Keypair generation");
|
||||
my $text = Newt::Textbox(70, 10, 0, $message);
|
||||
my $ret;
|
||||
|
||||
$text->TakesFocus(1);
|
||||
|
||||
$panel->Add(0, 0, $text);
|
||||
$panel->Add(0, 1, NextBackCancelButton());
|
||||
|
||||
$ret = &RunForm($panel);
|
||||
|
||||
$panel->Hide();
|
||||
undef $panel;
|
||||
|
||||
return $ret;
|
||||
Newt::Suspend();
|
||||
print STDOUT "$msg" if $msg;
|
||||
print STDOUT "$cmd $args"."\n";
|
||||
if ($debug) {
|
||||
system("gdb $cmd");
|
||||
} else {
|
||||
system("$cmd $args");
|
||||
}
|
||||
print STDERR "$cmd returned $!"."\n" if $!;
|
||||
Newt::Resume();
|
||||
}
|
||||
|
||||
#
|
||||
# makeCert
|
||||
# make certificate using the database
|
||||
#
|
||||
# Given a keyfile, expiry date, and set of certificate information
|
||||
# create a X509 certificate to make a key and store it
|
||||
#
|
||||
|
||||
sub makeCert
|
||||
sub makeCertNSS
|
||||
{
|
||||
my ($keyfile,$certfile,$cert,$days) = @_;
|
||||
use Fcntl;
|
||||
my ($certfile, # output
|
||||
$subject, $days, $nickname,
|
||||
$noisefile, $pwdfile) = @_;
|
||||
|
||||
# If no days specified it's a ca so use 2 years
|
||||
use integer;
|
||||
my $months = $days / 30;
|
||||
my $trustargs = "\"" . "TCu,TCu,TCuw". "\"";
|
||||
|
||||
my $cmd = "$bindir/certutil";
|
||||
my $args = "-S ";
|
||||
$args .= "-n $nickname ";
|
||||
$args .= "-s $subject ";
|
||||
$args .= "-x "; ## self-signed
|
||||
$args .= "-t $trustargs ";
|
||||
$args .= "-k rsa ";
|
||||
$args .= "-g $bits ";
|
||||
$args .= "-v $months ";
|
||||
$args .= "-f $pwdfile " if $pwdfile;
|
||||
$args .= "-z $noisefile ";
|
||||
$args .= "-d $modNssDbDir ";
|
||||
$args .= "-o $certfile";
|
||||
|
||||
nssUtilCmd($debug, $cmd, $args,
|
||||
"\nGenerating the cert\n\n");
|
||||
|
||||
$tempfile = "/tmp/rand.".$$;
|
||||
if (!sysopen(OUT, $tempfile, O_WRONLY|O_EXCL|O_CREAT)) {
|
||||
Newt::newtWinMessage("Fatal Error", "Close", "Could not write to ".
|
||||
"temporary file $tempfile");
|
||||
Newt::Finished();
|
||||
unlink($noisefile) unless $debug;
|
||||
|
||||
if (!-f $certfile) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Unable to create a certificate for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $field ('C', 'ST', 'L', 'O', 'OU', 'CN',
|
||||
'Challenge', 'CompanyName') {
|
||||
my $value = $cert{$field} || '.';
|
||||
print OUT "$value\n";
|
||||
# Create a certificate-signing request file that can be submitted to
|
||||
# a Certificate Authority for processing into a finished certificate.
|
||||
sub genRequestNSS
|
||||
{
|
||||
my ($csrfile, # output
|
||||
$subject, $days, $noisefile, $pwdfile) = @_;
|
||||
|
||||
use integer;
|
||||
my $months = $days / 30;
|
||||
|
||||
my $cmd = "$bindir/certutil";
|
||||
my $args = "-R ";
|
||||
|
||||
$args .= "-s $subject ";
|
||||
$args .= "-d $modNssDbDir ";
|
||||
$args .= "-a "; ## using ascii
|
||||
$args .= "-k rsa ";
|
||||
$args .= "-g $bits ";
|
||||
$args .= "-f $pwdfile ";
|
||||
$args .= "-v $months ";
|
||||
$args .= "-z $noisefile ";
|
||||
|
||||
$args .= " > $csrfile ";
|
||||
|
||||
nssUtilCmd($debug, $cmd, $args,
|
||||
"\nGenerating cert request (may take some time)\n\n");
|
||||
|
||||
unlink($noisefile) unless $debug;
|
||||
|
||||
if (!-f $csrfile) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Was not able to create a CSR for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
close(OUT);
|
||||
}
|
||||
|
||||
system("$bindir/openssl req -config $sslconf -new -key $keyfile $days -out $certfile < $tempfile 2> /dev/null");
|
||||
unlink($tempfile);
|
||||
# Generate a CA certificate file saving to private key to a file
|
||||
# Do not leave keys or certs in the database, use keyutil instaed of certutil.
|
||||
sub makeCertOpenSSL
|
||||
{
|
||||
my ($keyfile, $certfile, # output
|
||||
$subject, $days, $noisefile, $pwdfile) = @_;
|
||||
|
||||
use integer;
|
||||
my $months = $days ? $days / 30 : 24;
|
||||
my $keysize = $bits;
|
||||
|
||||
# build the arguments for a gen cert call, self-signed
|
||||
my $cmd = "$ssltop/keyutil";
|
||||
my $args = "-c makecert ";
|
||||
$args .= "-g $keysize ";
|
||||
$args .= "-s $subject ";
|
||||
$args .= "-v $months ";
|
||||
$args .= "-z $noisefile ";
|
||||
$args .= "-f $pwdfile " if $pwdfile; # sometimes there is no password
|
||||
$args .= "-o $certfile ";
|
||||
$args .= "-k $keyfile";
|
||||
|
||||
nssUtilCmd($debug, $cmd, $args,
|
||||
"\nPlease wait - generating the cert (this may take some time)\n\n");
|
||||
|
||||
if (!-f $certfile) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Was not able to create a certificate for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
"Was not able to create a certificate for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
unlink($noisefile) unless $debug;
|
||||
Newt::Finished();
|
||||
clearTempFiles() unless $debug;
|
||||
exit 1;
|
||||
}
|
||||
if ($keyfile && (-f $keyfile)) {
|
||||
if (chmod(0400, $keyfile) != 1) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Could not set permissions of private key file.\n".
|
||||
"$keyfile");
|
||||
Newt::Finished();
|
||||
unlink($noisefile) unless $debug;
|
||||
clearTempFiles() unless $debug;
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
unlink($randfile);
|
||||
}
|
||||
|
||||
# Create a certificate-signing request file that can be submitted to a
|
||||
# Certificate Authority (CA) for processing into a finished certificate.
|
||||
# Do not use the nss database, use keyutil instead of certutil. Export
|
||||
# the key if possible.
|
||||
sub genRequestOpenSSL
|
||||
{
|
||||
my ($keyfile,$csrfile, # output
|
||||
$subject,$days,$noisefile,$pwdfile) = @_;
|
||||
|
||||
use integer;
|
||||
my $months = $days ? $days / 30 : 24;
|
||||
|
||||
# build the arguments for a gen request call
|
||||
my $cmd="$ssltop/keyutil";
|
||||
my $args = " -c genreq ";
|
||||
$args .= "-g $bits ";
|
||||
$args .= "-s $subject ";
|
||||
$args .= "-v $months ";
|
||||
$args .= "-o $csrfile ";
|
||||
$args .= "-k $keyfile ";
|
||||
$args .= "-f $pwdfile " if $pwdfile;
|
||||
$args .= "-z $noisefile " if $noisefile;
|
||||
|
||||
nssUtilCmd($debug, $cmd, $args,
|
||||
"\nPlease wait - generating the request (may take some time)\n\n");
|
||||
|
||||
unlink($noisefile) unless $debug;
|
||||
Newt::Resume();
|
||||
|
||||
if (!-f $csrfile) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Unable to create a cert signing request for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
if ($keyfile && !(-f $keyfile)) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Unable to create a key for this ".
|
||||
"host:\n\nPress return to exit");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
if (chmod(0400, $keyfile) != 1) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Could not set permissions of private key file.\n".
|
||||
"$keyfile");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
my $passwordarg = $pwdfile ? "file:$pwdfile" : "pass:12345";
|
||||
Newt::Suspend();
|
||||
system("openssl pkcs8 -inform PEM -in $keyfile -passin $passwordarg");
|
||||
Newt::Resume();
|
||||
}
|
||||
}
|
||||
|
||||
sub AddField
|
||||
{
|
||||
@ -763,10 +985,6 @@ sub getCertDetails
|
||||
Newt::NEWT_ENTRY_SCROLL());
|
||||
|
||||
if ($iscsr) {
|
||||
# TODO: difficult to fit this message in and keep the form <25 rows
|
||||
# $subp->Add(0, 6, Newt::Label("Please enter the following 'extra' ".
|
||||
# "attributes\nto be sent with your ".
|
||||
# "certificate request."));
|
||||
|
||||
my $msg = "Extra attributes for certificate request:";
|
||||
|
||||
@ -815,10 +1033,22 @@ sub getCertDetails
|
||||
$cert{'O'} = $ents{'O'}->Get();
|
||||
$cert{'OU'} = $ents{'OU'}->Get();
|
||||
$cert{'CN'} = $ents{'CN'}->Get();
|
||||
|
||||
# Build the subject from the details
|
||||
|
||||
$SEP = ", ";
|
||||
$subject = 'CN' . "=" . $cert{'CN'};
|
||||
$subject = $subject . $SEP . 'OU' . "=" . $cert{'OU'} if $cert{'OU'};
|
||||
$subject = $subject . $SEP . 'O' . "=" . $cert{'O'} if $cert{'O'};
|
||||
$subject = $subject . $SEP . 'L' . "=" . $cert{'L'} if $cert{'L'};
|
||||
$subject = $subject . $SEP . 'ST' . "=" . $cert{'ST'} if $cert{'ST'};
|
||||
$subject = $subject . $SEP . 'C' . "=" . $cert{'C'} if $cert{'C'};
|
||||
|
||||
if ($iscsr) {
|
||||
$cert{'CompanyName'} = $ents{'CompanyName'}->Get();
|
||||
$cert{'Challenge'} = $ents{'Challenge'}->Get();
|
||||
$cert{'CompanyName'} = $ents{'CompanyName'}->Get();
|
||||
$cert{'Challenge'} = $ents{'Challenge'}->Get();
|
||||
$subject = $subject . $SEP . 'CompanyName' ."=" . $cert{'CompanyName'} if $cert{'CompanyName'};
|
||||
$subject = $subject . $SEP . 'Challenge' ."=" . $cert{'Challenge'} if $cert{'Challenge'};
|
||||
}
|
||||
|
||||
$panel->Hide();
|
||||
@ -826,7 +1056,9 @@ sub getCertDetails
|
||||
undef $subp;
|
||||
undef $panel;
|
||||
|
||||
$cadetails = $cert;
|
||||
# must escape the double quotes because
|
||||
# it will be embedded in another string
|
||||
$subject = "\"" . "$subject" . "\"";
|
||||
|
||||
return "Next";
|
||||
}
|
||||
@ -888,14 +1120,28 @@ sub genReqWindow
|
||||
my $ret = getCertDetails($servername,$msg, 1);
|
||||
return $ret unless ($ret eq "Next");
|
||||
|
||||
makeCert($keyfile,$csrfile,$cadetails,"");
|
||||
if ($nss) {
|
||||
genRequestNSS($csrfile,
|
||||
$subject, 730, $randfile, $tmpPasswordFile);
|
||||
} else {
|
||||
genRequestOpenSSL($keyfile, $csrfile,
|
||||
$subject, 730, $randfile, $tmpPasswordFile);
|
||||
}
|
||||
|
||||
# Now make a temporary cert
|
||||
|
||||
if (!$genreq_mode) {
|
||||
if (!-f $certfile) {
|
||||
makeCert($keyfile,$certfile,$cadetails,"-days $cert_days -x509");
|
||||
}
|
||||
if (!-f $certfile) {
|
||||
if ($nss) {
|
||||
makeCertNSS($certfile,
|
||||
$subject, $cert_days, $nickname,
|
||||
$randfile, $tmpPasswordFile);
|
||||
} else {
|
||||
makeCertOpenSSL($keyfile,$certfile,
|
||||
$subject, $cert_days,
|
||||
$randfile, $tmpPasswordFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
undef $csrtext;
|
||||
@ -998,7 +1244,15 @@ sub genCertWindow
|
||||
my $ret = getCertDetails($servername,$msg, 0);
|
||||
return $ret unless ($ret eq "Next");
|
||||
|
||||
makeCert($keyfile,$certfile,$cadetails,"-days $cert_days -x509");
|
||||
if ($nss) {
|
||||
makeCertNSS($certfile, # output
|
||||
$subject,$cert_days,$nickname,
|
||||
$randfile,$tmpPasswordFile);
|
||||
} else {
|
||||
makeCertOpenSSL($keyfile,$certfile, # output
|
||||
$subject,$cert_days,
|
||||
$randfile,$tmpPasswordFile);
|
||||
}
|
||||
|
||||
return "Next";
|
||||
}
|
||||
@ -1019,7 +1273,13 @@ sub genCACertWindow
|
||||
my $ret = getCertDetails("",$msg, 0);
|
||||
return $ret unless ($ret eq "Next");
|
||||
|
||||
makeCert($keyfile,$certfile,$cadetails,"-days 730 -x509");
|
||||
if ($nss) {
|
||||
makeCertNSS($certfile,$subject,730,$nickname,
|
||||
$randfile,$tmpPasswordFile);
|
||||
} else {
|
||||
makeCertOpenSSL($keyfile,$certfile,$subject,730,
|
||||
$randfile,$tmpPasswordFile);
|
||||
}
|
||||
|
||||
return "Next";
|
||||
}
|
||||
@ -1031,39 +1291,16 @@ sub getRandomDataWindow()
|
||||
# Get some random data from truerand library
|
||||
#
|
||||
if (!$skip_random) {
|
||||
FinishRoot();
|
||||
InitRoot(0);
|
||||
makerand($randbits,$randfile);
|
||||
FinishRoot();
|
||||
FinishRoot();
|
||||
InitRoot(0);
|
||||
makerand($randbits,$randfile);
|
||||
FinishRoot();
|
||||
|
||||
# Get some random data from keystrokes
|
||||
#
|
||||
Newt::Suspend();
|
||||
|
||||
system("$bindir/keyrand $randbits $randfile");
|
||||
} else {
|
||||
Newt::Suspend();
|
||||
Newt::Resume();
|
||||
}
|
||||
return "Next";
|
||||
}
|
||||
|
||||
sub generateKey()
|
||||
{
|
||||
print STDERR "\nPlease wait - generating the key (this may take some time)\n\n";
|
||||
|
||||
# Actually generate the key
|
||||
#
|
||||
system("$bindir/openssl genrsa -rand $randfile $bits > $keyfile");
|
||||
unlink($randfile);
|
||||
Newt::Resume();
|
||||
|
||||
if (chmod(0400, $keyfile) != 1) {
|
||||
Newt::newtWinMessage("Error", "Close",
|
||||
"Could not set permissions of private key file.\n".
|
||||
"$1 - $keyfile");
|
||||
Newt::Finished();
|
||||
exit 1;
|
||||
}
|
||||
|
||||
return "Skip";
|
||||
}
|
||||
|
12
genkey.xml
12
genkey.xml
@ -35,6 +35,10 @@
|
||||
<arg><option>--genreq</option></arg>
|
||||
<arg><option>--makeca</option></arg>
|
||||
</group>
|
||||
<group>
|
||||
<arg><option>--nss</option></arg>
|
||||
<arg><option>--debug</option>
|
||||
</group>
|
||||
<arg choice="req"><replaceable>hostname</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -47,7 +51,8 @@
|
||||
Signing Requests (CSR). Generated certificates are stored in the
|
||||
directory <filename>/etc/pki/tls/certs/</filename>, and the
|
||||
corresponding private key in
|
||||
<filename>/etc/pki/tls/private/</filename>. </para>
|
||||
<filename>/etc/pki/tls/private/</filename>. If using mod_nss
|
||||
they are stored in the nss key and certficate databases. </para>
|
||||
|
||||
<para><command>genkey</command> will prompt for the size of key
|
||||
desired; whether or not to generate a CSR; whether or not an
|
||||
@ -58,6 +63,11 @@
|
||||
private key using the truerand library and also by prompting the
|
||||
user for entry of random text.</para>
|
||||
|
||||
<para><option>nss</option> indicates that mod_nss database
|
||||
should be used to store keys and certificates.</para>
|
||||
|
||||
<para><option>debug</option> set this flag to enable debug logs.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
215
pemutil.c
Executable file
215
pemutil.c
Executable file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
Copyright 2008 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.
|
||||
|
||||
In addition, as a special exception, Red Hat, Inc. gives permission
|
||||
to link the code of this program with the OpenSSL library (or with
|
||||
modified versions of OpenSSL that use the same license as OpenSSL),
|
||||
and distribute linked combinations including the two. You must obey
|
||||
the GNU General Public License in all respects for all of the code
|
||||
used other than OpenSSL. If you modify this file, you may extend
|
||||
this exception to your version of the file, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version.
|
||||
|
||||
*/
|
||||
|
||||
/* Certificate processing utilities, based on code from Mozilla
|
||||
* Network Security Services internal secutils static library.
|
||||
*/
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Netscape security libraries.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*
|
||||
* The exported function here is PEMUTIL_PEM_read_X509. A function like
|
||||
* this belongs in nss_compat_ossl. Elio Maldonado <emaldona@redhat.com>
|
||||
*/
|
||||
|
||||
#include <cert.h>
|
||||
#include <certt.h>
|
||||
#include <nspr.h>
|
||||
#include <seccomon.h>
|
||||
#include <base64.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* TODO: When CERT_NewTempCertificate becomes public in NSS 3.12
|
||||
* remove this extern declaration and the following #define.
|
||||
*/
|
||||
extern CERTCertificate *
|
||||
__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
||||
char *nickname, PRBool isperm, PRBool copyDER);
|
||||
|
||||
#define CERT_NewTempCertificate __CERT_NewTempCertificate
|
||||
|
||||
|
||||
#define TIME_BUF_SIZE 100
|
||||
|
||||
/* decode a SECItem containing either a SEC_ASN1_GENERALIZED_TIME
|
||||
or a SEC_ASN1_UTC_TIME */
|
||||
extern SECStatus DER_DecodeTimeChoice(PRTime* output, const SECItem* input);
|
||||
|
||||
|
||||
/* Loads the contents of a file into a SECItem.
|
||||
* Code is from the NSS security utilities.
|
||||
*/
|
||||
static SECStatus FileToItem(SECItem *dst, PRFileDesc *src)
|
||||
{
|
||||
PRFileInfo info;
|
||||
PRInt32 numBytes;
|
||||
PRStatus prStatus;
|
||||
|
||||
prStatus = PR_GetOpenFileInfo(src, &info);
|
||||
|
||||
if (prStatus != PR_SUCCESS) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* XXX workaround for 3.1, not all utils zero dst before sending */
|
||||
dst->data = 0;
|
||||
if (!SECITEM_AllocItem(NULL, dst, info.size))
|
||||
goto loser;
|
||||
|
||||
numBytes = PR_Read(src, dst->data, info.size);
|
||||
if (numBytes != info.size)
|
||||
goto loser;
|
||||
|
||||
return SECSuccess;
|
||||
loser:
|
||||
SECITEM_FreeItem(dst, PR_FALSE);
|
||||
dst->data = NULL;
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Load a DER encoding into a SECItem.
|
||||
* Code is from the NSS security utilities.
|
||||
*/
|
||||
static SECStatus ReadDERFromFile(SECItem *der, PRFileDesc *inFile, PRBool ascii)
|
||||
{
|
||||
SECStatus rv;
|
||||
if (ascii) {
|
||||
/* First convert ascii to binary */
|
||||
SECItem filedata;
|
||||
char *asc, *body;
|
||||
|
||||
/* Read in ascii data */
|
||||
rv = FileToItem(&filedata, inFile);
|
||||
asc = (char *)filedata.data;
|
||||
if (!asc) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* check for headers and trailers and remove them */
|
||||
if ((body = strstr(asc, "-----BEGIN")) != NULL) {
|
||||
char *trailer = NULL;
|
||||
asc = body;
|
||||
body = PORT_Strchr(body, '\n');
|
||||
if (!body)
|
||||
body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */
|
||||
if (body)
|
||||
trailer = strstr(++body, "-----END");
|
||||
if (trailer != NULL) {
|
||||
*trailer = '\0';
|
||||
} else {
|
||||
/*printf("input has header but no trailer\n");*/
|
||||
PORT_Free(filedata.data);
|
||||
return SECFailure;
|
||||
}
|
||||
} else {
|
||||
body = asc;
|
||||
}
|
||||
|
||||
/* Convert to binary */
|
||||
rv = ATOB_ConvertAsciiToItem(der, body);
|
||||
if (rv) {
|
||||
/* printf("ATOB_ConvertAsciiToItem failed\n");*/
|
||||
PORT_Free(filedata.data);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
PORT_Free(filedata.data);
|
||||
} else {
|
||||
/* Read in binary der */
|
||||
rv = FileToItem(der, inFile);
|
||||
if (rv) {
|
||||
return SECFailure;
|
||||
}
|
||||
}
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
|
||||
/* Return a certificate structure from a pem-encoded cert in a file;
|
||||
* or NULL on failure. Semantics similar to an OpenSSL
|
||||
* PEM_read_X509(fp, NULL, NULL, NULL); call
|
||||
*/
|
||||
CERTCertificate *
|
||||
PEMUTIL_PEM_read_X509(const char *filename)
|
||||
{
|
||||
CERTCertificate *cert = NULL;
|
||||
PRFileDesc *fd = NULL;
|
||||
SECItem derCert;
|
||||
|
||||
fd = PR_Open(filename, PR_RDONLY, 0);
|
||||
if (!fd) return NULL;
|
||||
|
||||
/* Read in a DER from a file, it is ascii */
|
||||
if (SECSuccess != ReadDERFromFile(&derCert, fd, PR_TRUE))
|
||||
goto cleanup;
|
||||
|
||||
/* create a temporary cert in the database */
|
||||
cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
|
||||
&derCert, NULL, PR_FALSE, PR_FALSE);
|
||||
/* noNickname, notPerm, noCopy */
|
||||
cleanup:
|
||||
if (fd) PR_Close(fd);
|
||||
|
||||
return cert;
|
||||
}
|
Loading…
Reference in New Issue
Block a user