blob: 581ad06d1eb83d2c056be83176cbb95c04290774 [file] [log] [blame]
#!/usr/bin/perl
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#--------------------------------------------------------------
# cgi script that parses request argument to appropriate
# open ssl or tstclntw options and starts ssl client.
#
use CGI qw/:standard/;
use subs qw(debug);
#--------------------------------------------------------------
# Prints out an error string and exits the script with an
# exitStatus.
# Param:
# str : an error string
# exitStat: an exit status of the program
#
sub svr_error {
my ($str, $exitStat) = @_;
if (!defined $str || $str eq "") {
$str = $ERR;
}
print "SERVER ERROR: $str\n";
if ($exitStat) {
print end_html if ($osDataArr{wservRun});
exit $exitStat;
}
}
#--------------------------------------------------------------
# Prints out a debug message
# Params:
# str: debug message
# inVal: additional value to print(optional)
#
sub debug {
my ($str, $inVal) = @_;
print "-- DEBUG: $str ($inVal)\n" if ($DEBUG == 1);
}
#--------------------------------------------------------------
# Initializes execution context depending on a webserver the
# script is running under.
#
sub init {
%osDataArr = (
loadSupportedCipthersFn => \&osSpecific,
cipherIsSupportedFn => \&verifyCipherSupport,
cipherListFn => \&convertCipher,
buildCipherTableFn => \&buildCipherTable,
execCmdFn => \&osSpecific,
);
$scriptName = $ENV{'SCRIPT_NAME'};
if (!defined $scriptName) {
$DEBUG=1;
debug "Debug is ON";
}
$DEBUG=1;
$svrSoft = $ENV{'SERVER_SOFTWARE'};
if (defined $svrSoft) {
$_ = $svrSoft;
/.*Microsoft.*/ && ($osDataArr{wserv} = "IIS");
/.*Apache.*/ && ($osDataArr{wserv} = "Apache");
$osDataArr{wservRun} = 1;
} else {
$osDataArr{wserv} = "Apache";
$osDataArr{wservRun} = 0;
}
}
#--------------------------------------------------------------
# Function-spigot to handle errors is OS specific functions are
# not implemented for a particular OS.
# Returns:
# always returns 0(failure)
#
sub osSpecific {
$ERR = "This function should be swapped to os specific function.";
return 0;
}
#--------------------------------------------------------------
# Sets os specific execution context values.
# Returns:
# 1 upon success, or 0 upon failure(if OS was not recognized)
#
sub setFunctRefs {
debug("Entering setFunctRefs function", $osDataArr{wserv});
if ($osDataArr{wserv} eq "Apache") {
$osDataArr{osConfigFile} = "apache_unix.cfg";
$osDataArr{suppCiphersCmd} = '$opensslb ciphers ALL:NULL';
$osDataArr{clientRunCmd} = '$opensslb s_client -host $in_host -port $in_port -cert $certDir/$in_cert.crt -key $certDir/$in_cert.key -CAfile $caCertFile $proto $ciphers -ign_eof < $reqFile';
$osDataArr{loadSupportedCipthersFn} = \&getSupportedCipherList_Unix;
$osDataArr{execCmdFn} = \&execClientCmd_Unix;
} elsif ($osDataArr{wserv} eq "IIS") {
$osDataArr{osConfigFile} = "iis_windows.cfg";
$osDataArr{suppCiphersCmd} = '$tstclntwb';
$osDataArr{clientRunCmd} = '$tstclntwb -h $in_host -p $in_port -n $in_cert $proto $ciphers < $reqFile';
$osDataArr{loadSupportedCipthersFn} = \&getSupportedCipherList_Win;
$osDataArr{execCmdFn} = \&execClientCmd_Win;
} else {
$ERR = "Unknown Web Server type.";
return 0;
}
return 1;
}
#--------------------------------------------------------------
# Parses data from HTTP request. Will print a form if request
# does not contain sufficient number of parameters.
# Returns:
# 1 if request has sufficient number of parameters
# 0 if not.
sub getReqData {
my $debug = param('debug');
$in_host = param('host');
$in_port = param('port');
$in_cert = param('cert');
$in_cipher = param('cipher');
if (!$osDataArr{wservRun}) {
$in_host="goa1";
$in_port="443";
$in_cert="TestUser511";
$in_cipher = "SSL3_RSA_WITH_NULL_SHA";
}
debug("Entering getReqData function", "$in_port:$in_host:$in_cert:$in_cipher");
if (defined $debug && $debug == "debug on") {
$DEBUG = 1;
}
if (!defined $in_host || $in_host eq "" ||
!defined $in_port || $in_port eq "" ||
!defined $in_cert || $in_cert eq "") {
if ($osDataArr{wservRun}) {
print h1('Command description form:'),
start_form(-method=>"get"),
"Host: ",textfield('host'),p,
"Port: ",textfield('port'),p,
"Cert: ",textfield('cert'),p,
"Cipher: ",textfield('cipher'),p,
checkbox_group(-name=>'debug',
-values=>['debug on ']),
submit,
end_form,
hr;
} else {
print "Printing html form to get client arguments\n";
}
$ERR = "the following parameters are required: host, port, cert";
return 0;
} else {
print "<pre>" if ($osDataArr{wservRun});
return 1;
}
}
#--------------------------------------------------------------
# Building cipher conversion table from file based on the OS.
# Params:
# tfile: cipher conversion file.
# sysName: system name
# tblPrt: returned pointer to a table.
sub buildCipherTable {
my ($tfile, $sysName, $tblPrt) = @_;
my @retArr = @$tblPrt;
my %table, %rtable;
my $strCount = 0;
debug("Entering getReqData function", "$tfile:$sysName:$tblPrt");
($ERR = "No system name supplied" && return 0) if ($sysName =~ /^$/);
if (!open(TFILE, "$tfile")) {
$ERR = "Missing cipher conversion table file.";
return 0;
}
foreach (<TFILE>) {
chop;
/^#.*/ && next;
/^\s*$/ && next;
if ($strCount++ == 0) {
my @sysArr = split /\s+/;
$colCount = 0;
for (;$colCount <= $#sysArr;$colCount++) {
last if ($sysArr[$colCount] =~ /(.*:|^)$sysName.*/);
}
next;
}
my @ciphArr = split /\s+/, $_;
$table{$ciphArr[0]} = $ciphArr[$colCount];
$rtable{$ciphArr[$colCount]} = $ciphArr[0];
}
close(TFILE);
$cipherTablePtr[0] = \%table;
$cipherTablePtr[1] = \%rtable;
return 1
}
#--------------------------------------------------------------
# Client configuration function. Loads client configuration file.
# Initiates cipher table. Loads cipher list supported by ssl client.
#
sub configClient {
debug "Entering configClient function";
my $res = &setFunctRefs();
return $res if (!$res);
open(CFILE, $osDataArr{'osConfigFile'}) ||
($ERR = "Missing configuration file." && return 0);
foreach (<CFILE>) {
/^#.*/ && next;
chop;
eval $_;
}
close(CFILE);
local @cipherTablePtr = ();
$osDataArr{'buildCipherTableFn'}->($cipherTableFile, $clientSys) || return 0;
$osDataArr{cipherTable} = $cipherTablePtr[0];
$osDataArr{rcipherTable} = $cipherTablePtr[1];
local $suppCiphersTablePrt;
&{$osDataArr{'loadSupportedCipthersFn'}} || return 0;
$osDataArr{suppCiphersTable} = $suppCiphersTablePrt;
}
#--------------------------------------------------------------
# Verifies that a particular cipher is supported.
# Params:
# checkCipher: cipher name
# Returns:
# 1 - cipher is supported(also echos the cipher).
# 0 - not supported.
#
sub verifyCipherSupport {
my ($checkCipher) = @_;
my @suppCiphersTable = @{$osDataArr{suppCiphersTable}};
debug("Entering verifyCipherSupport", $checkCipher);
foreach (@suppCiphersTable) {
return 1 if ($checkCipher eq $_);
}
$ERR = "cipher is not supported.";
return 0;
}
#--------------------------------------------------------------
# Converts long(?name of the type?) cipher name to
# openssl/tstclntw cipher name.
# Returns:
# 0 if cipher was not listed. 1 upon success.
#
sub convertCipher {
my ($cipher) = @_;
my @retList;
my $resStr;
my %cipherTable = %{$osDataArr{cipherTable}};
debug("Entering convertCipher", $cipher);
if (defined $cipher) {
my $cphr = $cipherTable{$cipher};
if (!defined $cphr) {
$ERR = "cipher is not listed.";
return 0;
}
&{$osDataArr{'cipherIsSupportedFn'}}($cphr) || return 0;
$ciphers = "$cphr";
return 1;
}
return 0;
}
#################################################################
# UNIX Apache Specific functions
#----------------------------------------------------------------
#--------------------------------------------------------------
# Executes ssl client command to get a list of ciphers supported
# by client.
#
sub getSupportedCipherList_Unix {
my @arr, @suppCiphersTable;
debug "Entering getSupportedCipherList_Unix function";
eval '$sLisrCmd = "'.$osDataArr{'suppCiphersCmd'}.'"';
if (!open (OUT, "$sLisrCmd|")) {
$ERR="Can not run command to verify supported cipher list.";
return 0;
}
@arr = <OUT>;
chop $arr[0];
@suppCiphersTable = split /:/, $arr[0];
debug("Supported ciphers", $arr[0]);
$suppCiphersTablePrt = \@suppCiphersTable;
close(OUT);
return 1;
}
#--------------------------------------------------------------
# Lunches ssl client command in response to a request.
#
#
sub execClientCmd_Unix {
my $proto;
local $ciphers;
debug "Entering execClientCmd_Unix";
if (defined $in_cipher && $in_cipher ne "") {
my @arr = split /_/, $in_cipher, 2;
$proto = "-".$arr[0];
$proto =~ tr /SLT/slt/;
$proto = "-tls1" if ($proto eq "-tls");
return 0 if (!&{$osDataArr{'cipherListFn'}}($in_cipher));
$ciphers = "-cipher $ciphers";
debug("Return from cipher conversion", "$ciphers");
}
eval '$command = "'.$osDataArr{'clientRunCmd'}.'"';
debug("Executing command", $command);
if (!open CMD_OUT, "$command 2>&1 |") {
$ERR = "can not launch client";
return 0;
}
my @cmdOutArr = <CMD_OUT>;
foreach (@cmdOutArr) {
print $_;
}
my $haveVerify = 0;
my $haveErrors = 0;
foreach (@cmdOutArr) {
chop;
if (/unknown option/) {
$haveErrors++;
svr_error "unknown option\n";
next;
}
if (/:no ciphers available/) {
$haveErrors++;
svr_error "no cipthers available\n";
next;
}
if (/verify error:/) {
$haveErrors++;
svr_error "unable to do verification\n";
next;
}
if (/alert certificate revoked:/) {
$haveErrors++;
svr_error "attempt to connect with revoked sertificate\n";
next;
}
if (/(error|ERROR)/) {
$haveErrors++;
svr_error "found errors in server log\n";
next;
}
/verify return:1/ && ($haveVerify = 1);
}
if ($haveVerify == 0) {
svr_error "no 'verify return:1' found in server log\n";
$haveErrors++;
}
if ($haveErrors > 0) {
$ERR = "Have $haveErrors server errors";
debug "Exiting execClientCmd_Unix";
return 0;
}
debug "Exiting execClientCmd_Unix";
return 1;
}
#################################################################
# Windows IIS Specific functions
#----------------------------------------------------------------
#--------------------------------------------------------------
# Executes ssl client command to get a list of ciphers supported
# by client.
#
sub getSupportedCipherList_Win {
my @arr, @suppCiphersTable;
debug "Entering getSupportedCipherList_Win function";
eval '$sLisrCmd = "'.$osDataArr{'suppCiphersCmd'}.'"';
if (!open (OUT, "$sLisrCmd|")) {
$ERR="Can not run command to verify supported cipher list.";
return 0;
}
my $startCipherList = 0;
foreach (<OUT>) {
chop;
if ($startCipherList) {
/^([a-zA-Z])\s+/ && push @suppCiphersTable, $1;
next;
}
/.*from list below.*/ && ($startCipherList = 1);
}
debug("Supported ciphers", join ':', @suppCiphersTable);
$suppCiphersTablePrt = \@suppCiphersTable;
close(OUT);
return 1;
}
#--------------------------------------------------------------
# Lunches ssl client command in response to a request.
#
#
sub execClientCmd_Win {
my $proto;
local $ciphers;
debug "Entering execClientCmd_Win";
if (defined $in_cipher && $in_cipher ne "") {
my @arr = split /_/, $in_cipher, 2;
$proto = "-2 -3 -T";
$proto =~ s/-T// if ($arr[0] eq "TLS");
$proto =~ s/-3// if ($arr[0] eq "SSL3");
$proto =~ s/-2// if ($arr[0] eq "SSL2");
return 0 if (!&{$osDataArr{'cipherListFn'}}($in_cipher));
$ciphers = "-c $ciphers";
debug("Return from cipher conversion", $ciphers);
}
eval '$command = "'.$osDataArr{'clientRunCmd'}.'"';
debug("Executing command", $command);
if (!open CMD_OUT, "$command 2>&1 |") {
$ERR = "can not launch client";
return 0;
}
my @cmdOutArr = <CMD_OUT>;
foreach (@cmdOutArr) {
print $_;
}
my $haveVerify = 0;
my $haveErrors = 0;
foreach (@cmdOutArr) {
chop;
if (/unknown option/) {
$haveErrors++;
svr_error "unknown option\n";
next;
}
if (/Error performing handshake/) {
$haveErrors++;
svr_error "Error performing handshake\n";
next;
}
if (/Error creating credentials/) {
$haveErrors++;
svr_error "Error creating credentials\n";
next;
}
if (/Error .* authenticating server credentials!/) {
$haveErrors++;
svr_error "Error authenticating server credentials\n";
next;
}
if (/(error|ERROR|Error)/) {
$haveErrors++;
svr_error "found errors in server log\n";
next;
}
}
if ($haveErrors > 0) {
$ERR = "Have $haveErrors server errors";
debug "Exiting execClientCmd_Win";
return 0;
}
debug "Exiting execClientCmd_Win";
return 1;
}
#################################################################
# Main line of execution
#----------------------------------------------------------------
&init;
if ($osDataArr{wservRun}) {
print header('text/html').
start_html('iopr client');
}
print "SCRIPT=OK\n";
if (!&getReqData) {
svr_error($ERR, 1);
}
if (!&configClient) {
svr_error($ERR, 1);
}
&{$osDataArr{'execCmdFn'}} || svr_error;
if ($osDataArr{wservRun}) {
print "</pre>";
print end_html;
}