1

centos-6.8 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 ./blib/lib/CSP.pm:

. . .
package CSP;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

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.

$CSP::_openssl='openssl';

. . .

$CSP::_openssl='openssl';
. . .
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});
    $self->{openssl}->cmd('genrsa',$cmd,$args);
  }

 ## 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";
    }
  else
    {
      $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";

    $dir;
  }
. . .

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 $ENV{CSPDEBUG};
    if ($rw eq 's')
      {
  $self->{rc} = system("$self->{openssl} $cmd $engine $cfgcmd $cmdline ${redirect}");
      }
    else
      {
  open $self->{fh},"${lp}$self->{openssl} $cmd $engine $cfgcmd $cmdline ${redirect}${rp}" or
    $self->{csp}->die("Unable to execute: $!");
      }

    $self;
  }
. . .

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)

and

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

yields:

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

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?

James B. Byrne
  • 1,048
  • 12
  • 27
  • We're missing too much code to answer your questions. `$self` is the object, and `$self->{openssl}` is an attribute of that object. It in turn contains another object, because it's calling the method `cmd` on it. Please look for other occurrences of `$self->{openssl}`, preferably the one where it's defined. Also include the top of the program with all the `use` and `require` statements. – simbabque Oct 20 '16 at 19:35
  • You are right and I knew it from the outset. The whole script is available at the public github repository given in the introduction: https://github.com/byrnejb/rcsp . What I have posted here is overlong as it is. – James B. Byrne Oct 20 '16 at 19:44
  • And I discovered the technically correct answer to my question as posed. the system call is at the tail end of the script as shown in the additional code fragment I just posted. I just do not know how that portion of the script gets invoked from where the command line is created. And that is essentially what I need explained to me. – James B. Byrne Oct 20 '16 at 19:51
  • I updated the code fragments with the script header info requested. There are dozens of lines containing `$self->{openssl}`. What would the definition statement look like? – James B. Byrne Oct 20 '16 at 20:01
  • I found it already on github. It's a CSP::OpenSSL object. That class is defined in the same file. There are several `package`s in that one .pm file. [Here](https://github.com/byrnejb/rcsp/blob/master/CSP.pm#L1401) it is pushing to some handle. That might be where it issues the command. I'm still reading code. – simbabque Oct 20 '16 at 20:04
  • Start reading from https://github.com/byrnejb/rcsp/blob/master/CSP.pm#L1358. it uses [IPC::Run](https://metacpan.org/pod/IPC::Run) to open a pipe that it can read and write to in pieces. So the commands get issued where it `pump`s into the handle. The debug just shows the commands it runs. – simbabque Oct 20 '16 at 20:13
  • I am actually working on the csp040 branch. The file you found in the root directory is not used anywhere - it was just copied there at some point to aid in restoring things to their original condition in the blib/lib directory. It is gone from the working branch. That said I have located the statement in the working copy and am now reading on how this stuff is supposed to work. I will have other questions once I have digested the new material. – James B. Byrne Oct 20 '16 at 20:40
  • This is the line in the working copy of CSP.pm https://github.com/byrnejb/rcsp/blob/csp040/blib/lib/CSP.pm#L1442 – James B. Byrne Oct 25 '16 at 16:01
  • I tracked this problem down to the presence of single quotes surrounding the password input string. Without the quotes a password with a space in it causes the openssl comand to fail. With the quotes the openssl command works if the pas phrase contains a space but perl will not execute the openssl command. – James B. Byrne Oct 25 '16 at 18:56
  • @simbabque If you would like to enter the answer to this question, which is essentially found in your last comment, then I will mark it as correct. – James B. Byrne Oct 25 '16 at 20:24
  • I think you should answer the question yourself. If you feel you don't deserve the credit, make it community wiki, but I feel you did most of the work, so you should just answer yourself. You can quote my comment if it helped. ;) – simbabque Oct 26 '16 at 08:51
  • It helped. A great deal. – James B. Byrne Oct 26 '16 at 13:58

2 Answers2

0

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;
1424        
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.

James B. Byrne
  • 1,048
  • 12
  • 27
0

I'm the original author of that package and I abandoned it long ago for reasons that should be apparent. Go take a look at https://github.com/leifj/ici for something a bit more maintainable (even tho its just basically bash-scripts)

leifj
  • 1
  • 1