James B. Byrne James B. Byrne - 1 year ago 51
Perl Question

How and Where is OpenSSL called in this script?

perl, v5.10.1 (*) built for x86_64-linux-thread-multi

I am attempting to update a Perl script called CSP. My experience with the script is limited to running it on rare occasions when we needed a new server certificate. I contacted the author of the original script, Leif Johansson, but I did not receive a response. The revised project that I am presently working on and refer to below can be found at https://github.com/byrnejb/rcsp/tree/csp040.

That is the background. My programming experience with Perl is negligible. Thus my questions here may be naive.

I have these code fragments in


. . .
package CSP;

use strict;

require Exporter;
require AutoLoader;
use IO::File;
use Term::Prompt;
use POSIX qw(strftime);
use Date::Calc qw(Day_of_Week Gmtime Add_Delta_Days Add_Delta_DHMS);
use Sys::Hostname;

@ISA = qw(Exporter AutoLoader);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
@EXPORT = qw();
@EXPORT_OK = qw($_openssl);
$VERSION = '0.40';

# Preloaded methods go here.

# Autoload methods go after =cut, and are processed by the autosplit program.


. . .

. . .
sub genkey
my $self = shift;
my $args = shift;

$self->die("Required parameter keyfile missing")
unless $args->{keyfile};

$args->{keysize} = 4096 unless $args->{keysize} > 0;
$args->{keypass} = "'" . $self->getPassword("Private key password",1) . "'"
unless $args->{keypass};

$self->warn("# Password argument: $args->{keypass}\n") if $ENV{CSPDEBUG};

my $cmd = "-out $args->{keyfile} $args->{keysize}";
$cmd = "-des3 -passout pass:$args->{keypass} ".$cmd if defined($args->{keypass});

## Generate and optionally self-sign the request
my $process;
my $what;
my $common_args = "-$args->{digest} -days $args->{days} ".
" -key $cakey -passin pass:$args->{keypass}";
if ($args->{csrfile})
$self->{openssl}->cmd('req',"-new $common_args -out $args->{csrfile}",$args);
$what = "generated CA request for";
$self->{openssl}->cmd('req',"-x509 $common_args -new -out $cacert",$args);
$what = "initialized self-signed";

$self->warn("Successfully $what CA $self->{name}")
if $args->{verbose};

sub checkCA
my $self = shift;
my $dir = $self->caDir();

$self->die("Uninitialized CA: missing or unreadable ca certificate in $dir")
unless -r "$dir/ca.crt";

$self->die("Uninitialized CA: missing or unreadable ca private key in $dir")
unless -r "$dir/private/ca.key";

. . .

And towards the end of the script file this:

. . .
$self->{csp} = $csp;

$cmd = '' if $cmd eq 'dummy';

my $engine = "-engine opensc" if $ENV{CSP_OPENSC};

my $redirect = ($args->{verbose} == 0 && $rw ne 'r' ? ">/dev/null 2>&1" : "");
warn "${lp}$self->{openssl} $cmd $cfgcmd $cmdline ${redirect}${rp}"
if ($rw eq 's')
$self->{rc} = system("$self->{openssl} $cmd $engine $cfgcmd $cmdline ${redirect}");
open $self->{fh},"${lp}$self->{openssl} $cmd $engine $cfgcmd $cmdline ${redirect}${rp}" or
$self->{csp}->die("Unable to execute: $!");

. . .

When I run this using the following command line with debugging on:

csp HLL_ROOT init \
--keysize=4096 \
--days=7318 \
--url=ca.harte-lyne.ca \
--email=certificates@harte-lyne.ca \
--digest=sha512 \
--verbose \
"CN=HLL_ROOT,OU=Networked Data Services,O=Harte & Lyne Limited,L=Hamilton,ST=Ontario,C=CA,DC=harte-lyne,DC=ca"

Then I see this:

openssl genrsa -des3 -passout pass:'a test' -out /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/private/ca.key 4096

followed by:

openssl genrsa -des3 -passout pass:'a test' -out /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/private/ca.key 4096

and ending with:

[CSP][HLL_ROOT] Successfully initialized self-signed CA HLL_ROOT

However, the expected outputs of ca.key and ca.crt are not found in the directories shown as arguments in the commands above.

$ find /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT -name ca\.\*

Yet, if I copy and paste those exact commands into my bash session shell they work.

openssl genrsa -des3 -passout pass:'a test' -out /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/private/ca.key 4096
Generating RSA private key, 4096 bit long modulus
e is 65537 (0x10001)


openssl req -config /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/tmp/csp-8154.conf -x509 -sha512 -days 7318 -key /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/private/ca.key -passin pass:'a test' -new -out /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT/ca.crt


$ find /home/byrnejb/Projects/Software/rcsp/ca_test_a/csp/HLL_ROOT -name ca\.\*

That seems to me that the commands are being created properly but that the openssl utility is not being called. As there is no branching code in the section where these commands are generated I conclude that the construct
$self->{openssl}->cmd('req',"-x509 $common_args -new -out $cacert",$args);
is the actual call to openssl but I do not know how this is meant to work.

How is this supposed to work? Why is it not working?

And should not the return code from openssl be checked?

Answer Source

Per @simbabque comment the place that the openssl call is made is here:

1398    use IPC::Run qw( start pump finish timeout new_appender new_chunker);
        . . .
1418    sub cmd
1419      {
1420        my $self = shift;
1421        my $cmd = shift;
1422        my $cmdline = shift;
1423        my $args = shift;
1425        my $conf;
1426        my $cfgcmd;
        . . .
1448        $self->{_handle}->pump while length ${$self->{_in}};
        . . .

The underlying difficulty is the use of embedded white-space in the pass-phrase. As written the code passes arguments to IPC:Run as a concatenated string. For arguments passed as a string IPC:Run uses white-space as an argument delimiter. The correct method of dealing with this is to refactor the code to use an array to pass arguments instead.