Use nss library for cryptography (#346731)

This commit is contained in:
Elio Maldonado 2008-05-01 01:25:10 +00:00
parent 40da9fa7c0
commit f65b51384d
8 changed files with 966 additions and 233 deletions

View File

@ -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' > $@

View File

@ -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, &notBefore, &notAfter)
!= 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;
}

View File

@ -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

View File

@ -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>

View File

@ -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
View File

@ -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";
}

View File

@ -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
View 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;
}