#!/usr/bin/perl
#
# Create CA and Intermediate CA certs

use AppConfig qw(:argcount :expand);
use Carp;
use File::Slurp;
use File::Temp qw/ tempfile /;
use Getopt::Long;
use IPC::Run qw ( run timeout );
use Pod::Usage;
use strict;
use Term::ReadPassword;

my $opt_caname;
my $opt_caroot;
my $opt_conf;
my $opt_debug;
my $opt_example;
my $opt_help;
my $opt_intername;
my $opt_manual;
my $opt_root;

my $CONF;

##############################################################################
# Helper routines
##############################################################################

# ------------------------------------------------------------------------
# Read configuration file.  The configuration file is used to set the
# command line switch variables, i.e. opt_*, for selected values.
sub read_config {
    my ($filename) = @_;
    if ($opt_debug) {
        dbg("Configuration file ($filename)");
    }

    $CONF = AppConfig->new({});
    $CONF->define('capass',    { ARGCOUNT => ARGCOUNT_ONE });
    $CONF->define('caname',    { ARGCOUNT => ARGCOUNT_ONE });
    $CONF->define('caroot',    { ARGCOUNT => ARGCOUNT_ONE });
    $CONF->define('debug',     { ARGCOUNT => ARGCOUNT_NONE });
    $CONF->define('interpass', { ARGCOUNT => ARGCOUNT_ONE });
    $CONF->define('intername', { ARGCOUNT => ARGCOUNT_ONE });

    if (-e $filename) {
        if ($opt_debug) {
            dbg("Reading $filename");
        }
        $CONF->file($filename);
    }

    # Process command line options
    if ($opt_caname)    { $CONF->caname($opt_caname); }
    if ($opt_intername) { $CONF->intername($opt_intername); }
    if ($opt_caroot)    { $CONF->caroot($opt_caroot); }
    if ($opt_debug)     { $CONF->debug($opt_debug); }

    # Make sure we have a password
    if (!$CONF->capass) {
        my $pw = read_password('CA password:');
        if (!$pw) {
            prt("ERROR: CA Password is required.");
            pod2usage(-verbose => 0);
        }
        $CONF->capass($pw);
    }

    # Validate configuration
    if ($CONF->intername && !$CONF->interpass) {
        my $pw = read_password('Intermediate CA password:');
        if (!$pw) {
            prt("ERROR: Intermediate CA Password is required.");
            pod2usage(-verbose => 0);
        }
        $CONF->interpass($pw);
    }
    if (!$CONF->caroot) {
        $CONF->caroot('/etc/caroot');
    }
    return;
}

# ------------------------------------------------------------------------
# Print out an example configuration file and exit.
sub example_config {

    prt("# make-ca example (/etc/make-ca.conf or ./make-ca.con)\n");
    prt("#\n");
    prt("# The password used to protect the key of CA\n");
    prt("capass = FirstPassword\n");
    prt("#\n");
    prt("# The CN of the certificate authority\n");
    prt("caname = Master CA\n");
    prt("#\n");
    prt("# The root directory where CA files are created.\n");
    prt("caroot = /etc/caroot\n");
    prt("#\n");
    prt("# Show debugging outout\n");
    prt("debug = 1\n");
    prt("#\n");
    prt("# The password that protects the key of the intermediate CA\n");
    prt("interpass = SecondPassword\n");
    prt("#\n");
    prt("# The CN of the intermediate certificate authority\n");
    prt("intername = Sub-Master CA\n");

    return;
}

# ------------------------------------------------------------------------
# Make sure a directory exists
sub check_dir {
    my ($dir) = @_;
    if (!-d $dir) {
        mkdir $dir or die "ERROR: problem creating directory ($dir)\n";
    }
    return;
}

#------------------------------------------------------------------------
# Generate a subject for the certificate

sub gen_subject {
    my ($cn) = @_;
    my $cert_subject
      = '/C=US'
      . '/ST=CA'
      . '/L=Half Moon Bay'
      . '/O=MacAllister Software'
      . '/OU=IT'
      . "/CN=$cn";
    return $cert_subject;
}

#------------------------------------------------------------------------
# run a shell command line

sub run_cmd {
    my @cmd = @_;

    my $in;
    my $out;
    my $err;
    prt("Executing: " . join(' ', @cmd) . "\n");
    eval { run(\@cmd, \$in, \$out, \$err, timeout(30)); };
    if ($@) {
        if ($err) {
            $err .= "\n";
        }
        $err .= 'ERROR executing:' . join(q{ }, @cmd) . "\n";
        $err .= $@;
        croak "$err\n";
    }
    if ($CONF->debug) {
        if ($out) {
            prt("$out\n");
        }
        if ($err) {
            prt("INFO: $err\n");
        }
    }
    return $out;
}

#------------------------------------------------------------------------
# print and die if there are errors.
sub prt {
    my ($txt) = @_;
    print {*STDOUT} $txt or croak "Problem writing to STDOUT";
    return;
}

#------------------------------------------------------------------------
# debugging output
sub dbg {
    my ($txt) = @_;
    prt("DEBUG:$txt\n");
    return;
}

#------------------------------------------------------------------------
# Write a password alone to a temporary file that can be passed
# to openssl.  It is the caller's responsibility to delete the
# file when it is no longer useful.

sub save_tmp_password {
    my ($pass) = @_;
    my ($ph, $tmp_file) = tempfile();
    print $ph "${pass}\n"
      or croak "ERROR: problem writing to $tmp_file\n";
    close $ph
      or croak "ERROR: problem closing $tmp_file\n";
    return $tmp_file;
}

#------------------------------------------------------------------------
# Write the passwords to files
sub save_password {
    my ($key, $pass, $pass_file) = @_;

    # Create a file that can be used for parameter substitutions by
    # other scripts.
    my $fh;
    open($fh, '>', $pass_file)
      or croak("ERROR: problem opening to $pass_file\n");
    print {$fh} "$key = $pass\n"
      or croak("ERROR: problem writing to $pass_file\n");
    close $fh
      or croak("ERROR: problem closing $pass_file\n");
    chmod 0600, $pass_file
      or croak("ERROR: problem setting permissions on $pass_file\n");

    # Password file for use on command lines
    my $tmp_file = save_tmp_password($pass);

    return $tmp_file;
}

#------------------------------------------------------------------------
# Check that a file exists and is not zero length, otherwise konk over
# dead.
sub check_file {
    my ($f, $result) = @_;
    if (!-e $f) {
        prt("$result\n");
        die "ERROR: $f not found\n";
    }
    if (-z $f) {
        prt("$result\n");
        die "ERROR: $f is zero length\n";
    }
    return;
}

#------------------------------------------------------------------------
# Generate directories for a CA
sub generate_dirs {
    my ($root) = @_;
    my $fh;

    # Create directories
    check_dir($root);
    check_dir("${root}/certs");
    check_dir("${root}/crl");
    check_dir("${root}/newcerts");
    check_dir("${root}/private");

    # set permissions on private directory
    chmod 0700, "${root}/private"
      or croak("ERROR: problem setting permissions on ${root}/private\n");

    # Create index.txt
    my $ca_index_path = "${root}/index.txt";
    open($fh, '>', $ca_index_path)
      or croak("ERROR: problem opening ${ca_index_path} for write\n");
    close $fh;

    # Create serial
    my $ca_serial_path = "${root}/serial";
    open($fh, '>', $ca_serial_path)
      or croak("ERROR: problem opening ${ca_serial_path} for write\n");
    print $fh "1000\n";
    close $fh;

    return;
}

#------------------------------------------------------------------------
# Standardize the path name by creating them in one place.
sub set_paths {
    my ($caroot, $caname, $inname) = @_;

    my %paths        = ();
    my $ca_name_root = $caname;
    $ca_name_root =~ s/\s+//xmsg;

    $paths{ca_conf}  = "${caroot}/openssl.conf";
    $paths{ca_cert}  = "${caroot}/certs/${ca_name_root}.pem";
    $paths{ca_crl}   = "${caroot}/crlnumber";
    $paths{ca_crlno} = "${caroot}/crl/${ca_name_root}.crl.pem";
    $paths{ca_key}   = "${caroot}/private/${ca_name_root}.key.pem";
    $paths{ca_pass}  = "${caroot}/private/${ca_name_root}.password";
    $paths{ca_root}  = $caroot;

    my $inroot = $caroot;
    $inroot =~ s{/$}{}xms;
    $inroot .= '-intermediate';
    my $in_name_root = $inname;
    $in_name_root =~ s/\s+//xmsg;

    $paths{in_conf}  = "${inroot}/openssl.conf";
    $paths{in_cert}  = "${inroot}/certs/${in_name_root}.pem";
    $paths{in_chain} = "${inroot}/certs/${in_name_root}-chain.pem";
    $paths{in_crl}   = "${inroot}/crlnumber";
    $paths{in_crlno} = "${inroot}/crl/${in_name_root}.crl.pem";
    $paths{in_csr}   = "${inroot}/private/${in_name_root}.csr.pem";
    $paths{in_key}   = "${inroot}/private/${in_name_root}.key.pem";
    $paths{in_pass}  = "${inroot}/private/${in_name_root}.password";
    $paths{in_root}  = $inroot;

    return %paths;
}

#------------------------------------------------------------------------
# Here Doc that is the CA configuration
sub ca_conf {
    my ($path_ref) = @_;
    my %path = %{$path_ref};

    my $ca_conf = <<"END_CA_CONF";
[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
certs             = $path{ca_root}/certs
crl_dir           = $path{ca_root}/crl
new_certs_dir     = $path{ca_root}/newcerts
database          = $path{ca_root}/index.txt
serial            = $path{ca_root}/serial
RANDFILE          = $path{ca_root}/private/.rand

# The root key and root certificate.
private_key       = $path{ca_key}
certificate       = $path{ca_cert}

# For certificate revocation lists.
crlnumber         = $path{ca_crlno}
crl               = $path{ca_crl}
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_strict

[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits            = 2048
distinguished_name      = req_distinguished_name
string_mask             = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md              = sha256

# Extension to add when the -x509 option is used.
x509_extensions         = v3_ca

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName             = Country Name (2 letter code)
stateOrProvinceName     = State or Province Name
localityName            = Locality Name
0.organizationName      = Organization Name
organizationalUnitName  = Organizational Unit Name
commonName              = Common Name
emailAddress            = Email Address

# Optionally, specify some defaults.
countryName_default             = US
stateOrProvinceName_default     = California
localityName_default            = Half Moon Bay
0.organizationName_default      = MacAllister Software
organizationalUnitName_default  = IT

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:true, pathlen:0
keyUsage               = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints       = CA:FALSE
nsCertType             = client, email
nsComment              = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer
keyUsage          = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage       = clientAuth, emailProtection

[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints       = CA:FALSE
nsCertType             = server
nsComment              = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage               = critical, digitalSignature, keyEncipherment
extendedKeyUsage       = serverAuth

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
END_CA_CONF

    return $ca_conf;
}

#------------------------------------------------------------------------
# Here Doc that is the intermediate CA configuration
sub intermediate_conf {
    my ($path_ref) = @_;
    my %path = %{$path_ref};

    my $intermediate_conf = <<"END_INTERMEDIATE_CONF";
[ ca ]
# `man ca`
default_ca = CA_default

[ CA_default ]
# Directory and file locations.
certs             = $path{in_root}/certs
crl_dir           = $path{in_root}/crl
new_certs_dir     = $path{in_root}/newcerts
database          = $path{in_root}/index.txt
serial            = $path{in_root}/serial
RANDFILE          = $path{in_root}/private/.rand

# The root key and root certificate.
private_key       = $path{in_key}
certificate       = $path{in_cert}

# For certificate revocation lists.
crlnumber         = $path{in_crlno}
crl               = $path{in_crl}
crl_extensions    = crl_ext
default_crl_days  = 30

# SHA-1 is deprecated, so use SHA-2 instead.
default_md        = sha256

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_loose

[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# Allow the intermediate CA to sign a more diverse range of certificates.
# See the POLICY FORMAT section of the `ca` man page.
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
# Options for the `req` tool (`man req`).
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only

# SHA-1 is deprecated, so use SHA-2 instead.
default_md          = sha256

# Extension to add when the -x509 option is used.
x509_extensions     = v3_ca

[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

# Optionally, specify some defaults.
countryName_default             = US
stateOrProvinceName_default     = California
localityName_default            = Half Moon Bay
0.organizationName_default      = MacAllister Software
organizationalUnitName_default  = IT
#emailAddress_default           =

[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection

[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[ crl_ext ]
# Extension for CRLs (`man x509v3_config`).
authorityKeyIdentifier=keyid:always

[ ocsp ]
# Extension for OCSP signing certificates (`man ocsp`).
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
END_INTERMEDIATE_CONF

    return $intermediate_conf;
}

#------------------------------------------------------------------------
# Generate a root CA
sub generate_root_ca {
    my ($root, $caname, $capass) = @_;

    # File handle
    my $fh;

    # Command output
    my $result;

    # Generate the required directories
    generate_dirs($root);

    # Set paths used to create files.  Only pass the ca root since
    my %path = set_paths($root, $caname);

    # Create CA configuration file
    open($fh, '>', $path{ca_conf})
      or croak("ERROR: problem opening $path{ca_conf}\n");
    print $fh ca_conf(\%path);
    close $fh;

    my @cmd = ();

    prt("Saving CA key password.\n");
    my $tmp_pwfile = save_password('CACERTPASSWORD', $capass, $path{ca_pass});

    prt("\n");
    prt("Generating the CA key.\n");
    @cmd = ();
    push @cmd, 'openssl';
    push @cmd, 'genrsa';
    push @cmd, '-aes256';
    push @cmd, '-out',     $path{ca_key};
    push @cmd, '-passout', "file:$tmp_pwfile";
    push @cmd, '4096';
    $result = run_cmd(@cmd);
    check_file($path{ca_key}, $result);
    chmod 0600, $path{ca_key}
      or croak("ERROR: problem setting permissions on $path{ca_key}\n");

    prt("\n");
    prt("Generating the CA certificate.\n");
    # Create a cn for the certificate
    my $ca_subject = gen_subject($caname);
    @cmd = ();
    push @cmd, 'openssl';
    push @cmd, 'req';
    push @cmd, '-batch';
    push @cmd, '-config', $path{ca_conf};
    push @cmd, '-key',    $path{ca_key};
    push @cmd, '-new';
    push @cmd, '-x509';
    push @cmd, '-days', '7300';
    push @cmd, '-sha256';
    push @cmd, '-extensions', 'v3_ca';
    push @cmd, '-subj',       $ca_subject;
    push @cmd, '-passin',     "file:$tmp_pwfile";
    push @cmd, '-out',        $path{ca_cert};
    $result = run_cmd(@cmd);
    check_file($path{ca_cert}, $result);
    chmod 0444, $path{ca_cert}
      or croak("ERROR: problem setting permissions on $path{ca_cert}\n");

    if (-e $tmp_pwfile) {
        unlink $tmp_pwfile;
    }
    return;
}

#------------------------------------------------------------------------
# Generate an intermediate CA certificate
sub generate_intermediate_ca {
    my ($caroot, $caname, $capass, $inname, $inpass) = @_;

    my @cmd;
    my $fh;
    my $result;

    my $root = $caroot;
    $root =~ s{/$}{}xms;
    $root .= '-intermediate';

    # Generate the required directories
    generate_dirs($root);

    # TODO: Find CN of the root ca and make sure it does not match the
    # intermediate name.

    # Get file path names
    my %path = set_paths($caroot, $caname, $inname);

    # Create crlnumber file
    open($fh, '>', $path{in_crl})
      or croak("ERROR: problem opening $path{in_crl} for write\n");
    print $fh "1000\n";
    close $fh;

    # Create intermediate configuration file
    open($fh, '>', $path{in_conf})
      or croak("ERROR: problem opening $path{in_conf} for write\n");
    print $fh intermediate_conf(\%path);
    close $fh;

    # Save the intermediate password for reference
    prt("Saving Intermediate CA key password.\n");
    my $tmp_pwfile
      = save_password('INTERCACERTPASSWORD', $inpass, $path{in_pass});

    # Save the ca password for command line use
    my $tmp_ca_pwfile = save_tmp_password($capass);

    prt("\n");
    prt("Generating the Intermediate CA key.\n");
    @cmd = ();
    push @cmd, 'openssl';
    push @cmd, 'genrsa';
    push @cmd, '-aes256';
    push @cmd, '-out',     $path{in_key};
    push @cmd, '-passout', "file:$tmp_pwfile";
    push @cmd, '4096';
    $result = run_cmd(@cmd);
    check_file($path{in_key}, $result);
    chmod 0600, $path{in_key}
      or croak("ERROR: problem setting permissions on $path{in_key}\n");

    prt("\n");
    prt("Generating the Intermediate CA request.\n");
    my $in_subject = gen_subject($inname);
    @cmd = ();
    push @cmd, 'openssl';
    push @cmd, 'req';
    push @cmd, '-batch';
    push @cmd, '-config', $path{in_conf};
    push @cmd, '-new';
    push @cmd, '-sha256';
    push @cmd, '-key',    $path{in_key};
    push @cmd, '-out',    $path{in_csr};
    push @cmd, '-subj',   $in_subject;
    push @cmd, '-passin', "file:${tmp_pwfile}";
    $result = run_cmd(@cmd);
    check_file($path{in_csr}, $result);

    prt("\n");
    prt("Signing the Intermediate CA request.\n");
    @cmd = ();
    push @cmd, 'openssl';
    push @cmd, 'ca';
    push @cmd, '-batch';
    push @cmd, '-config',     $path{ca_conf};
    push @cmd, '-extensions', 'v3_intermediate_ca';
    push @cmd, '-days',       '3650';
    push @cmd, '-notext';
    push @cmd, '-md',     'sha256';
    push @cmd, '-in',     $path{in_csr};
    push @cmd, '-out',    $path{in_cert};
    push @cmd, '-passin', "file:${tmp_ca_pwfile}";
    $result = run_cmd(@cmd);
    check_file($path{in_cert}, $result);
    chmod 0444, $path{in_cert}
      or croak("ERROR: problem setting permissions on $path{in_cert}\n");

    # Create the chained certificate file
    prt("\n");
    prt("Creating the chained Intermediate CA certificate.\n");
    my $fh;
    my $in_ca_string = read_file($path{in_cert});
    my $ca_string    = read_file($path{ca_cert});
    open($fh, '>', $path{in_chain})
      or croak("ERROR: problem writing to $path{in_chain}\n");
    print $fh $in_ca_string;
    print $fh $ca_string;
    close $fh or croak("ERROR: unable to close $path{in_chain}\n");

    # Delete the password files now that we don't need it.
    if (-e $tmp_ca_pwfile) {
        unlink $tmp_ca_pwfile
          or croak("ERROR: problem deleteing $tmp_ca_pwfile\n");
    }
    if (-e $tmp_pwfile) {
        unlink $tmp_pwfile
          or croak("ERROR: problem deleteing $tmp_pwfile\n");
    }

    return;
}

##############################################################################
# Main routine
##############################################################################

GetOptions(
    'caname=s'    => \$opt_caname,
    'caroot=s'    => \$opt_caroot,
    'conf=s'      => \$opt_conf,
    'debug'       => \$opt_debug,
    'example'     => \$opt_example,
    'help'        => \$opt_help,
    'intername=s' => \$opt_intername,
    'manual'      => \$opt_manual
);

# Flush output immediately
$| = 1;

# help the poor souls out
if ($opt_help || $ARGV[0]) {
    pod2usage(-verbose => 0);
}
if ($opt_manual) {
    pod2usage(-verbose => 2);
}
if ($opt_example) {
    example_config();
    exit 1;
}

# read configuration file if it exists
my $conf_file;
if ($opt_conf) {
    $conf_file = $opt_conf;
} else {
    $conf_file = '/etc/make-ca.conf';
}
read_config($conf_file);

# CA name and password are always required
if (!$CONF->caname) {
    prt("ERROR: caname is required.\n");
    pod2usage(-verbose => 0);
}

if ($CONF->intername) {

    # -----------------------------
    # Generating an intermediate CA
    # Intermediate CA password is required

    # The CA must already exist
    if (!-e $CONF->caroot) {
        prt("ERROR: caroot directory does not exists.\n");
        pod2usage(-verbose => 0);
    }
    # Require the caname for now.  This should be pulled from the
    # CA cert at some point.
    if (!$CONF->caname) {
        prt("ERROR: caname is required\n");
        pod2usage(-verbose => 0);
    }
    # The CA name and the Intermediate CA name must not be the same
    if ($CONF->caname eq $CONF->intername) {
        prt("ERROR: caname and intername must not be the same\n");
        pod2usage(-verbose => 0);
    }
    generate_intermediate_ca(
        $CONF->caroot,    $CONF->caname, $CONF->capass,
        $CONF->intername, $CONF->interpass
    );

} else {

    # -----------------------------
    # Generating a root CA

    # Make sure nothing is being over written
    if (-e $CONF->caroot) {
        prt("ERROR: caroot directory already exists.\n");
        pod2usage(-verbose => 0);
    }
    generate_root_ca($CONF->caroot, $CONF->caname, $CONF->capass);
}

exit;

__END__

=head1 NAME

make-ca - make a certificate authority

=head1 SYNOPSIS

make-ca [--conf=<filename>] [--example] [--caroot=>directory path>]
[--caname=<subject>] [--intername=<subject>] [--help] [--manual]
[--debug]

=head1 DESCRIPTION

This script can be used to create root CA key or an intermediate CA
based on an existing root ca.

In most cases the script will takes input from either command line
switches or the file /etc/make-ca.conf.  Password can be entered in the
configuration file.  If not present in the configuration file the
script will prompt for passwords as needed.

=head1 OPTIONS AND ARGUMENTS

=over 4

=item --conf=<filename>

The configuration file to use.  If not supplied the script will attempt
to read /etc/make-ca.conf.

=item --caname=<subject>

The CN of the subject to use in the certificate and the root name of
files generated.  The CA name will be stripped of embedded spaces and
used to form file names of the certificate, the key, and the password
for the key.  The certiciate name is required.

=item --caroot=<path>

The root directory where the certificate will be stored.  The default
is /etc/caroot.  Certificates will be written to <path>/certs.  The
key and csr will be written to <path>/private.  The output directories
must not exist when creating a CA and must exist when creating an
intermediate CA.

For intermeidate certificates the path where files are create is
formed from the root CA name and is of the form <rootpath>-intermediate/.

=item --example

Print an example configuration file to standard out.

=item --intername=<subject>

The CN of the subject to use in the intermediate certificate and the
root name of files generated.  The CA name will be stripped of
embedded spaces and used to form file names of the certificate, the
key, and the password for the key.  The certiciate name is required
when generating an intermediate CA.  If the intermediate CA password
is not present in the configuration file it will be prompted for.

=item --debug

Generate debugging messages.

=item --help

A short help message.

=item --manual

The complete documentation.

=back

=head1 EXAMPLES

=over 2

=item Generate a root CA

make-ca --caroot=/etc/testca2 --caname="Test Root CA"

=item Generate an intermediate CA

make-ca --caroot=/etc/testca2 --caname="Test Root CA" --intername="LDAP Test Intermediate CA"

=back

=head1 AUTHOR

Bill MacAllister <bill@ca-zephyr.org>

=head1 COPYRIGHT

This software was developed for use at Shelter Cove.  All rights
reserved 2016.

=cut
