From f65b51384d46c25b5458d92972afd6e9cabcdfb6 Mon Sep 17 00:00:00 2001 From: Elio Maldonado Date: Thu, 1 May 2008 01:25:10 +0000 Subject: [PATCH] Use nss library for cryptography (#346731) --- Makefile | 43 +++- certwatch.c | 308 +++++++++++++++++++++------ certwatch.cron | 56 +++-- certwatch.xml | 11 + crypto-utils.spec | 33 ++- genkey.pl | 521 +++++++++++++++++++++++++++++++++------------- genkey.xml | 12 +- pemutil.c | 215 +++++++++++++++++++ 8 files changed, 966 insertions(+), 233 deletions(-) create mode 100755 pemutil.c diff --git a/Makefile b/Makefile index 2774475..c855004 100644 --- a/Makefile +++ b/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' > $@ diff --git a/certwatch.c b/certwatch.c index b288ee2..4dd8e54 100644 --- a/certwatch.c +++ b/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 , 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 */ -#include -#include +/* Replaced usage of OpenSSL with NSS. + * Elio Maldonado */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -41,55 +91,109 @@ #include #include +#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; } diff --git a/certwatch.cron b/certwatch.cron index e32743e..28ed392 100644 --- a/certwatch.cron +++ b/certwatch.cron @@ -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 diff --git a/certwatch.xml b/certwatch.xml index 356e0af..b97f2c3 100644 --- a/certwatch.xml +++ b/certwatch.xml @@ -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. + + The certificate can be specified by its nickname or by a + path to the containing file. @@ -86,6 +89,14 @@ default is root. + + , + + + Specify the database directory containing the certificate + and key database files. The default is yet to be determined. + + diff --git a/crypto-utils.spec b/crypto-utils.spec index a9de531..ec58305 100644 --- a/crypto-utils.spec +++ b/crypto-utils.spec @@ -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\";|" \ diff --git a/genkey.pl b/genkey.pl index 49e8529..1006a78 100644 --- a/genkey.pl +++ b/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 = ; + 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 = ; + 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 = <$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 = <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"; -} diff --git a/genkey.xml b/genkey.xml index 36ee0a0..d6d9147 100644 --- a/genkey.xml +++ b/genkey.xml @@ -35,6 +35,10 @@ + + + + hostname @@ -47,7 +51,8 @@ Signing Requests (CSR). Generated certificates are stored in the directory /etc/pki/tls/certs/, and the corresponding private key in - /etc/pki/tls/private/. + /etc/pki/tls/private/. If using mod_nss + they are stored in the nss key and certficate databases. genkey 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. + indicates that mod_nss database + should be used to store keys and certificates. + + set this flag to enable debug logs. + diff --git a/pemutil.c b/pemutil.c new file mode 100755 index 0000000..fa90ab8 --- /dev/null +++ b/pemutil.c @@ -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 , 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 + */ + +#include +#include +#include +#include +#include +#include + +/* 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; +}