Refactor file writing - introduce template driven file writing
apps/CA.pl and tools/c_rehash are built from template files. So far,
this was done by Configure, which created its own problems as it
forced everyone to reconfigure just because one of the template files
had changed.
Instead, have those files created as part of the normal build in apps/
and in tools/.
Furthermore, this prepares for a future where Configure may produce
entirely other build files than Makefile, and the latter can't be
guaranteed to be the holder of all information for other scripts.
Instead, configdata.pm (described below) becomes the center of
configuration information.
This introduces a few new things:
%config a hash table to hold all kinds of configuration data
that can be used by any other script.
configdata.pm a perl module that Configure writes. It currently
holds the hash tables %config and %target.
util/dofile.pl a script that takes a template on STDIN and outputs
the result after applying configuration data on it.
It's supposed to be called like this:
perl -I$(TOP) -Mconfigdata < template > result
or
perl -I$(TOP) -Mconfigdata templ1 templ2 ... > result
Note: util/dofile.pl requires Text::Template.
As part of this changed, remove a number of variables that are really
just copies of entries in %target, and use %target directly. The
exceptions are $target{cflags} and $target{lflags}, they do get copied
to $cflags and $lflags. The reason for this is that those variable
potentially go through a lot of changes and would rather deserve a
place in %config. That, however, is for another commit.
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Richard Levitte <levitte@openssl.org>
2015-05-18 20:35:23 +00:00
|
|
|
#!{- $config{perl} -}
|
1999-01-01 00:54:48 +00:00
|
|
|
#
|
2015-05-01 01:44:40 +00:00
|
|
|
# Wrapper around the ca to make it easier to use
|
2016-01-25 20:19:59 +00:00
|
|
|
#
|
|
|
|
# {- join("\n# ", @autowarntext) -}
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
my $openssl = "openssl";
|
|
|
|
if(defined $ENV{'OPENSSL'}) {
|
|
|
|
$openssl = $ENV{'OPENSSL'};
|
2005-02-01 23:48:37 +00:00
|
|
|
} else {
|
2015-05-01 01:44:40 +00:00
|
|
|
$ENV{'OPENSSL'} = $openssl;
|
2005-02-01 23:48:37 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
my $verbose = 1;
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-10-27 19:11:48 +00:00
|
|
|
my $OPENSSL_CONFIG = $ENV{"OPENSSL_CONFIG"};
|
2015-05-01 01:44:40 +00:00
|
|
|
my $DAYS = "-days 365";
|
|
|
|
my $CADAYS = "-days 1095"; # 3 years
|
2015-10-27 19:11:48 +00:00
|
|
|
my $REQ = "$openssl req $OPENSSL_CONFIG";
|
|
|
|
my $CA = "$openssl ca $OPENSSL_CONFIG";
|
2015-05-01 01:44:40 +00:00
|
|
|
my $VERIFY = "$openssl verify";
|
|
|
|
my $X509 = "$openssl x509";
|
|
|
|
my $PKCS12 = "$openssl pkcs12";
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
# default openssl.cnf file has setup as per the following
|
|
|
|
my $CATOP = "./demoCA";
|
|
|
|
my $CAKEY = "cakey.pem";
|
|
|
|
my $CAREQ = "careq.pem";
|
|
|
|
my $CACERT = "cacert.pem";
|
|
|
|
my $CACRL = "crl.pem";
|
|
|
|
my $DIRMODE = 0777;
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
my $NEWKEY = "newkey.pem";
|
|
|
|
my $NEWREQ = "newreq.pem";
|
|
|
|
my $NEWCERT = "newcert.pem";
|
|
|
|
my $NEWP12 = "newcert.p12";
|
|
|
|
my $RET = 0;
|
|
|
|
my $WHAT = shift @ARGV;
|
|
|
|
my $FILE;
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
# See if reason for a CRL entry is valid; exit if not.
|
|
|
|
sub crl_reason_ok
|
|
|
|
{
|
|
|
|
my $r = shift;
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
if ($r eq 'unspecified' || $r eq 'keyCompromise'
|
|
|
|
|| $r eq 'CACompromise' || $r eq 'affiliationChanged'
|
|
|
|
|| $r eq 'superseded' || $r eq 'cessationOfOperation'
|
|
|
|
|| $r eq 'certificateHold' || $r eq 'removeFromCRL') {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
print STDERR "Invalid CRL reason; must be one of:\n";
|
|
|
|
print STDERR " unspecified, keyCompromise, CACompromise,\n";
|
|
|
|
print STDERR " affiliationChanged, superseded, cessationOfOperation\n";
|
|
|
|
print STDERR " certificateHold, removeFromCRL";
|
|
|
|
exit 1;
|
1999-01-01 00:54:48 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
# Copy a PEM-format file; return like exit status (zero means ok)
|
|
|
|
sub copy_pemfile
|
|
|
|
{
|
|
|
|
my ($infile, $outfile, $bound) = @_;
|
|
|
|
my $found = 0;
|
1999-01-01 00:54:48 +00:00
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
open IN, $infile || die "Cannot open $infile, $!";
|
|
|
|
open OUT, ">$outfile" || die "Cannot write to $outfile, $!";
|
|
|
|
while (<IN>) {
|
|
|
|
$found = 1 if /^-----BEGIN.*$bound/;
|
|
|
|
print OUT $_ if $found;
|
|
|
|
$found = 2, last if /^-----END.*$bound/;
|
|
|
|
}
|
|
|
|
close IN;
|
|
|
|
close OUT;
|
|
|
|
return $found == 2 ? 0 : 1;
|
2014-09-04 20:59:44 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
# Wrapper around system; useful for debugging. Returns just the exit status
|
|
|
|
sub run
|
|
|
|
{
|
|
|
|
my $cmd = shift;
|
|
|
|
print "====\n$cmd\n" if $verbose;
|
|
|
|
my $status = system($cmd);
|
|
|
|
print "==> $status\n====\n" if $verbose;
|
|
|
|
return $status >> 8;
|
1999-01-01 00:54:48 +00:00
|
|
|
}
|
2015-05-01 01:44:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
if ( $WHAT =~ /^(-\?|-h|-help)$/ ) {
|
|
|
|
print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n";
|
|
|
|
print STDERR " CA -pkcs12 [certname]\n";
|
|
|
|
print STDERR " CA -crl|-revoke cert-filename [reason]\n";
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
if ($WHAT eq '-newcert' ) {
|
|
|
|
# create a certificate
|
|
|
|
$RET = run("$REQ -new -x509 -keyout $NEWKEY -out $NEWCERT $DAYS");
|
|
|
|
print "Cert is in $NEWCERT, private key is in $NEWKEY\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-newreq' ) {
|
|
|
|
# create a certificate request
|
|
|
|
$RET = run("$REQ -new -keyout $NEWKEY -out $NEWREQ $DAYS");
|
|
|
|
print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-newreq-nodes' ) {
|
|
|
|
# create a certificate request
|
|
|
|
$RET = run("$REQ -new -nodes -keyout $NEWKEY -out $NEWREQ $DAYS");
|
|
|
|
print "Request is in $NEWREQ, private key is in $NEWKEY\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-newca' ) {
|
|
|
|
# create the directory hierarchy
|
|
|
|
mkdir ${CATOP}, $DIRMODE;
|
|
|
|
mkdir "${CATOP}/certs", $DIRMODE;
|
|
|
|
mkdir "${CATOP}/crl", $DIRMODE ;
|
|
|
|
mkdir "${CATOP}/newcerts", $DIRMODE;
|
|
|
|
mkdir "${CATOP}/private", $DIRMODE;
|
|
|
|
open OUT, ">${CATOP}/index.txt";
|
|
|
|
close OUT;
|
|
|
|
open OUT, ">${CATOP}/crlnumber";
|
|
|
|
print OUT "01\n";
|
|
|
|
close OUT;
|
|
|
|
# ask user for existing CA certificate
|
|
|
|
print "CA certificate filename (or enter to create)\n";
|
|
|
|
$FILE = <STDIN>;
|
Perl's chop / chomp considered bad, use a regexp instead
Once upon a time, there was chop, which somply chopped off the last
character of $_ or a given variable, and it was used to take off the
EOL character (\n) of strings.
... but then, you had to check for the presence of such character.
So came chomp, the better chop which checks for \n before chopping it
off. And this worked well, as long as Perl made internally sure that
all EOLs were converted to \n.
These days, though, there seems to be a mixture of perls, so lines
from files in the "wrong" environment might have \r\n as EOL, or just
\r (Mac OS, unless I'm misinformed).
So it's time we went for the more generic variant and use s|\R$||, the
better chomp which recognises all kinds of known EOLs and chops them
off.
A few chops were left alone, as they are use as surgical tools to
remove one last slash or one last comma.
NOTE: \R came with perl 5.10.0. It means that from now on, our
scripts will fail with any older version.
Reviewed-by: Rich Salz <rsalz@openssl.org>
2016-02-11 20:47:30 +00:00
|
|
|
$FILE = s|\R$|| if $FILE;
|
2015-05-01 01:44:40 +00:00
|
|
|
if ($FILE) {
|
|
|
|
copy_pemfile($FILE,"${CATOP}/private/$CAKEY", "PRIVATE");
|
|
|
|
copy_pemfile($FILE,"${CATOP}/$CACERT", "CERTIFICATE");
|
|
|
|
} else {
|
|
|
|
print "Making CA certificate ...\n";
|
|
|
|
$RET = run("$REQ -new -keyout"
|
|
|
|
. " ${CATOP}/private/$CAKEY"
|
|
|
|
. " -out ${CATOP}/$CAREQ");
|
|
|
|
$RET = run("$CA -create_serial"
|
|
|
|
. " -out ${CATOP}/$CACERT $CADAYS -batch"
|
|
|
|
. " -keyfile ${CATOP}/private/$CAKEY -selfsign"
|
|
|
|
. " -extensions v3_ca"
|
|
|
|
. " -infiles ${CATOP}/$CAREQ") if $RET == 0;
|
|
|
|
print "CA certificate is in ${CATOP}/$CACERT\n" if $RET == 0;
|
|
|
|
}
|
|
|
|
} elsif ($WHAT eq '-pkcs12' ) {
|
|
|
|
my $cname = $ARGV[1];
|
|
|
|
$cname = "My Certificate" unless defined $cname;
|
|
|
|
$RET = run("$PKCS12 -in $NEWCERT -inkey $NEWKEY"
|
|
|
|
. " -certfile ${CATOP}/$CACERT"
|
|
|
|
. " -out $NEWP12"
|
|
|
|
. " -export -name \"$cname\"");
|
|
|
|
print "PKCS #12 file is in $NEWP12\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-xsign' ) {
|
|
|
|
$RET = run("$CA -policy policy_anything -infiles $NEWREQ");
|
|
|
|
} elsif ($WHAT eq '-sign' ) {
|
|
|
|
$RET = run("$CA -policy policy_anything -out $NEWCERT -infiles $NEWREQ");
|
|
|
|
print "Signed certificate is in $NEWCERT\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-signCA' ) {
|
|
|
|
$RET = run("$CA -policy policy_anything -out $NEWCERT"
|
|
|
|
. " -extensions v3_ca -infiles $NEWREQ");
|
|
|
|
print "Signed CA certificate is in $NEWCERT\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-signcert' ) {
|
|
|
|
$RET = run("$X509 -x509toreq -in $NEWREQ -signkey $NEWREQ"
|
|
|
|
. " -out tmp.pem");
|
|
|
|
$RET = run("$CA -policy policy_anything -out $NEWCERT"
|
|
|
|
. " -infiles tmp.pem") if $RET == 0;
|
|
|
|
print "Signed certificate is in $NEWCERT\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-verify' ) {
|
2015-05-01 11:11:17 +00:00
|
|
|
my @files = @ARGV ? @ARGV : ( $NEWCERT );
|
|
|
|
my $file;
|
2015-05-01 01:44:40 +00:00
|
|
|
foreach $file (@files) {
|
2016-01-13 14:13:02 +00:00
|
|
|
my $status = run("$VERIFY \"-CAfile\" ${CATOP}/$CACERT $file");
|
2015-05-01 01:44:40 +00:00
|
|
|
$RET = $status if $status != 0;
|
|
|
|
}
|
|
|
|
} elsif ($WHAT eq '-crl' ) {
|
|
|
|
$RET = run("$CA -gencrl -out ${CATOP}/crl/$CACRL");
|
|
|
|
print "Generated CRL is in ${CATOP}/crl/$CACRL\n" if $RET == 0;
|
|
|
|
} elsif ($WHAT eq '-revoke' ) {
|
|
|
|
my $cname = $ARGV[1];
|
|
|
|
if (!defined $cname) {
|
|
|
|
print "Certificate filename is required; reason optional.\n";
|
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
my $reason = $ARGV[2];
|
|
|
|
$reason = " -crl_reason $reason"
|
|
|
|
if defined $reason && crl_reason_ok($reason);
|
|
|
|
$RET = run("$CA -revoke \"$cname\"" . $reason);
|
|
|
|
} else {
|
|
|
|
print STDERR "Unknown arg \"$WHAT\"\n";
|
|
|
|
print STDERR "Use -help for help.\n";
|
|
|
|
exit 1;
|
1999-01-01 00:54:48 +00:00
|
|
|
}
|
|
|
|
|
2015-05-01 01:44:40 +00:00
|
|
|
exit $RET;
|