3

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

This question descends from this one Where is the shell command called which invokes OpenSSL commands?. Briefly I am hacking a very old Perl script used to maintain an internal private PKI so that the default signature hashes and key sizes meet current browser requirements.

I have these snippets of code:

. . .    
$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);
. . .
$self->{openssl}->cmd('req',"-x509 $common_args -new -out $cacert",$args);
. . .
use IPC::Run qw( start pump finish timeout new_appender new_chunker);
. . .
sub cmd
  {
    my $self = shift;
    my $cmd = shift;
    my $cmdline = shift;
    my $args = shift;
    my $conf;
    my $cfgcmd;
. . .
    $self->{_handle}->pump while length ${$self->{_in}};
. . .

If the password argument value that the user provides contains no white space then this code performs as desired. If it does contain embedded white space then the code fails silently. If the argument passed to keypass is concatenated with starting and ending single-quotes then the code likewise fails silently. In both cases of failure the script nonetheless reports success.

Why?

What change is necessary to make this code work whether the user input contains spaces or not?

simbabque
  • 53,749
  • 8
  • 73
  • 136
James B. Byrne
  • 1,048
  • 12
  • 27
  • Let me just get the question correct, you are trying to eliminate any whitespace from the password field in order to ensure it works? or specifically to make it work even if there is whitespace in the password? –  Oct 26 '16 at 06:54
  • @user2082599 from the previous question and the description here, I believe the password may contain whitespace. If it does, the program fails because the whitespace gets lost in the command line arguments. And quoting it doesn't work. – simbabque Oct 26 '16 at 09:01
  • @James can you please highlight which of the arguments is the password? That's not clear to me. – simbabque Oct 26 '16 at 09:01
  • @simbabque: I'm pretty sure it's `$args->{keypass}`. – Ilmari Karonen Oct 26 '16 at 09:02
  • BTW, note that passing passwords on the command line is generally considered insecure: if any untrusted users can run code on the same server (even under an unprivileged account), they can see the password simply by running `ps ax`. The [openssl manual](https://www.openssl.org/docs/manmaster/apps/openssl.html#Pass-Phrase-Options) notes this, and warns that `pass:` "should only be used where security is not important." – Ilmari Karonen Oct 26 '16 at 09:12
  • @IlmariKaronen the irony being that _ssl_ sort of implies that security is important ;) – simbabque Oct 26 '16 at 09:20
  • You could try if https://v1.metacpan.org/pod/String::ShellQuote does the right thing for you. – simbabque Oct 26 '16 at 09:21

1 Answers1

1

To answer your literal question, let me quote the IPC::Run manual:

"run(), start(), and harness() can all take a harness specification as input. A harness specification is either a single string to be passed to the systems' shell […] or a list of commands, io operations, and/or timers/timeouts to execute."

To prevent the command arguments being parsed by the shell (which is what's causing things to break when the arguments contain spaces), you should not pass them as a single string, but as a reference to an array that contains each individual argument as a single string, something like this:

my @cmd = ("-out", $args->{keyfile}, $args->{keysize});
unshift @cmd, ("-des3", "-passout", "pass:$args->{keypass}") if defined $args->{keypass};
# ...
my $h = start ["openssl", "genrsa", @cmd], \$in, \$out;  # or something equivalent

(The code you've posted seems to be using IPC::Run via some custom interface layer; since you haven't shown us exactly what that layer looks like, I've replace it with a simple call to IPC::Run::start.)


In any case, note that passing passwords on the command line is generally considered insecure: if any untrusted users can run code on the same server (even under an unprivileged account), they can see the password simply by running ps ax. The openssl manual notes this, and warns that pass:password "should only be used where security is not important."

A safer alternative would be to send the password over a separate file descriptor. Conveniently, IPC::Run makes this pretty easy:

my @cmd = ("-out", $args->{keyfile}, $args->{keysize});
unshift @cmd, ("-des3", "-passout", "fd:3") if defined $args->{keypass};
# ...
my $h = start ["openssl", "genrsa", @cmd], '<', \$in, '>', \$out, '3<', \$args->{keypass};

Here, the password is passed over the file descriptor number 3; if you need to pass in multiple passwords, you can use file descriptors 4, 5, etc. for those. (Descriptors 0, 1 and 2 are stdin, stdout and stderr.)

Disclaimer: This is, obviously, all untested code. I'm not an expert on IPC::Run, so I may have made some silly command syntax errors or other mistakes. Please test thoroughly before using!

Community
  • 1
  • 1
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • The difficulty I have is that is all very unfamiliar to me. I am hacking at a very old third party script written in a language of which I have only cursory knowledge; and whose original author has moved on and abandoned it. I needed to get this to work with a few minor changes to the default values for key sizes and change the signature hash algorithms to meet current expectations. Unfortunately for me I stumbled over this problem by accident and being me would like to get a proper solution. The easy work-around is simply do not use a pass-phrase with white-space. But that seems wrong. – James B. Byrne Oct 26 '16 at 13:38
  • @JamesB.Byrne: Unfortunately, some of the parts that you'll need to modify are in the code that you replaced with `. . .` in your question. Specifically, there's presumably an IPC::Run `start()` call somewhere in there that you need to modify to take an array reference instead of a string. This does probably need at least basic familiarity with Perl. If you don't have that, your choices are basically 1) grab a good introductory Perl book and learn, 2) find someone else who knows more Perl than you do, or 3) give up, document the bug and move on. – Ilmari Karonen Oct 26 '16 at 19:05
  • First I need to establish our replacement PKI. At the moment it appears that this will not be a problem since I seem to have surmounted or evaded the roadblocks I encountered. A rewrite of the code to switch the entire program from string to array arguments is not possible until I find the time, which resource is in minuscule supply. I can change the pass-phrase on the key directly using openssl which gives the desired result and avoids having to reprogram the script -- a process that would no doubt uncover other warts. – James B. Byrne Oct 26 '16 at 20:20