Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 2532 lines (2208 sloc) 81.303 kB
#!/usr/bin/perl -w
use File::Copy;
use File::Path;
use IO::Socket;
use Data::Dumper;
use Getopt::Long 'GetOptions';
use strict;
#==================== config =====================
my $logfile = 'test.log';
my $local_key_file = 'local_spa.key';
my $output_dir = 'output';
my $lib_dir = '../lib/.libs';
my $conf_dir = 'conf';
my $run_dir = 'run';
my $configure_path = '../configure';
my $cmd_out_tmp = 'cmd.out';
my $server_cmd_tmp = 'server_cmd.out';
my $gpg_client_home_dir = "$conf_dir/client-gpg";
my $nat_conf = "$conf_dir/nat_fwknopd.conf";
my $default_conf = "$conf_dir/default_fwknopd.conf";
my $default_access_conf = "$conf_dir/default_access.conf";
my $gpg_access_conf = "$conf_dir/gpg_access.conf";
my $default_digest_file = "$run_dir/digest.cache";
my $default_pid_file = "$run_dir/fwknopd.pid";
my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
my $require_user_access_conf = "$conf_dir/require_user_access.conf";
my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
my $require_src_access_conf = "$conf_dir/require_src_access.conf";
my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
my $fwknopCmd = '../client/.libs/fwknop';
my $fwknopdCmd = '../server/.libs/fwknopd';
my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
my $valgrindCmd = '/usr/bin/valgrind';
my $gpg_server_key = '361BBAD4';
my $gpg_client_key = '6A3FAD56';
my $loopback_ip = '127.0.0.1';
my $fake_ip = '127.0.0.2';
my $internal_nat_host = '192.168.1.2';
my $default_spa_port = 62201;
my $non_std_spa_port = 12345;
my $spoof_user = 'testuser';
#================== end config ===================
my $passed = 0;
my $failed = 0;
my $executed = 0;
my $test_include = '';
my @tests_to_include = ();
my $test_exclude = '';
my @tests_to_exclude = ();
my $list_mode = 0;
my $loopback_intf = '';
my $anonymize_results = 0;
my $current_test_file = "$output_dir/init";
my $tarfile = 'test_fwknop.tar.gz';
my $server_test_file = '';
my $use_valgrind = 0;
my $valgrind_str = '';
my $saved_last_results = 0;
my $diff_mode = 0;
my $enable_recompilation_warnings_check = 0;
my $sudo_path = '';
my $platform = '';
my $help = 0;
my $YES = 1;
my $NO = 0;
my $PRINT_LEN = 68;
my $USE_PREDEF_PKTS = 1;
my $USE_CLIENT = 2;
my $REQUIRED = 1;
my $OPTIONAL = 0;
my $NEW_RULE_REQUIRED = 1;
my $REQUIRE_NO_NEW_RULE = 2;
my $NEW_RULE_REMOVED = 1;
my $REQUIRE_NO_NEW_REMOVED = 2;
my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
my @args_cp = @ARGV;
exit 1 unless GetOptions(
'Anonymize-results' => \$anonymize_results,
'fwknop-path=s' => \$fwknopCmd,
'fwknopd-path=s' => \$fwknopdCmd,
'libfko-path=s' => \$libfko_bin,
'loopback-intf=s' => \$loopback_intf,
'test-include=s' => \$test_include,
'include=s' => \$test_include, ### synonym
'test-exclude=s' => \$test_exclude,
'exclude=s' => \$test_exclude, ### synonym
'enable-recompile-check' => \$enable_recompilation_warnings_check,
'List-mode' => \$list_mode,
'enable-valgrind' => \$use_valgrind,
'valgrind-path=s' => \$valgrindCmd,
'diff' => \$diff_mode,
'help' => \$help
);
&usage() if $help;
### create an anonymized tar file of test suite results that can be
### emailed around to assist in debugging fwknop communications
exit &anonymize_results() if $anonymize_results;
&identify_loopback_intf();
$valgrind_str = "$valgrindCmd --leak-check=full --show-reachable=yes --track-origins=yes" if $use_valgrind;
my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose";
my $default_client_gpg_args = "$default_client_args " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir";
my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
"-d $default_digest_file -p $default_pid_file";
my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopdCmd -c $default_conf " .
"-a $gpg_access_conf $intf_str " .
"-d $default_digest_file -p $default_pid_file";
### point the compiled binaries at the local libary path
### instead of any installed libfko instance
$ENV{'LD_LIBRARY_PATH'} = $lib_dir;
### main array that defines the tests we will run
my @tests = (
{
'category' => 'recompilation',
'detail' => 'recompile and look for compilation warnings',
'err_msg' => 'compile warnings exist',
'function' => \&compile_warnings,
'fatal' => $NO
},
{
'category' => 'build',
'subcategory' => 'client',
'detail' => 'binary exists',
'err_msg' => 'binary not found',
'function' => \&binary_exists,
'binary' => $fwknopCmd,
'fatal' => $YES
},
{
'category' => 'build security',
'subcategory' => 'client',
'detail' => 'Position Independent Executable (PIE)',
'err_msg' => 'non PIE binary (fwknop client)',
'function' => \&pie_binary,
'binary' => $fwknopCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'client',
'detail' => 'stack protected binary',
'err_msg' => 'non stack protected binary (fwknop client)',
'function' => \&stack_protected_binary,
'binary' => $fwknopCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'client',
'detail' => 'fortify source functions',
'err_msg' => 'source functions not fortified (fwknop client)',
'function' => \&fortify_source_functions,
'binary' => $fwknopCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'client',
'detail' => 'read-only relocations',
'err_msg' => 'no read-only relocations (fwknop client)',
'function' => \&read_only_relocations,
'binary' => $fwknopCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'client',
'detail' => 'immediate binding',
'err_msg' => 'no immediate binding (fwknop client)',
'function' => \&immediate_binding,
'binary' => $fwknopCmd,
'fatal' => $NO
},
{
'category' => 'build',
'subcategory' => 'server',
'detail' => 'binary exists',
'err_msg' => 'binary not found',
'function' => \&binary_exists,
'binary' => $fwknopdCmd,
'fatal' => $YES
},
{
'category' => 'build security',
'subcategory' => 'server',
'detail' => 'Position Independent Executable (PIE)',
'err_msg' => 'non PIE binary (fwknopd server)',
'function' => \&pie_binary,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'server',
'detail' => 'stack protected binary',
'err_msg' => 'non stack protected binary (fwknopd server)',
'function' => \&stack_protected_binary,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'server',
'detail' => 'fortify source functions',
'err_msg' => 'source functions not fortified (fwknopd server)',
'function' => \&fortify_source_functions,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'server',
'detail' => 'read-only relocations',
'err_msg' => 'no read-only relocations (fwknopd server)',
'function' => \&read_only_relocations,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'server',
'detail' => 'immediate binding',
'err_msg' => 'no immediate binding (fwknopd server)',
'function' => \&immediate_binding,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'build',
'subcategory' => 'libfko',
'detail' => 'binary exists',
'err_msg' => 'binary not found',
'function' => \&binary_exists,
'binary' => $libfko_bin,
'fatal' => $YES
},
{
'category' => 'build security',
'subcategory' => 'libfko',
'detail' => 'stack protected binary',
'err_msg' => 'non stack protected binary (libfko)',
'function' => \&stack_protected_binary,
'binary' => $libfko_bin,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'libfko',
'detail' => 'fortify source functions',
'err_msg' => 'source functions not fortified (libfko)',
'function' => \&fortify_source_functions,
'binary' => $libfko_bin,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'libfko',
'detail' => 'read-only relocations',
'err_msg' => 'no read-only relocations (libfko)',
'function' => \&read_only_relocations,
'binary' => $libfko_bin,
'fatal' => $NO
},
{
'category' => 'build security',
'subcategory' => 'libfko',
'detail' => 'immediate binding',
'err_msg' => 'no immediate binding (libfko)',
'function' => \&immediate_binding,
'binary' => $libfko_bin,
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'client',
'detail' => 'usage info',
'err_msg' => 'could not get usage info',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'client',
'detail' => 'getopt() no such argument',
'err_msg' => 'getopt() allowed non-existant argument',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
'exec_err' => $YES,
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'client',
'detail' => 'expected code version',
'err_msg' => 'code version mis-match',
'function' => \&expected_code_version,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'server',
'detail' => 'usage info',
'err_msg' => 'could not get usage info',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'server',
'detail' => 'getopt() no such argument',
'err_msg' => 'getopt() allowed non-existant argument',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
'exec_err' => $YES,
'fatal' => $NO
},
{
'category' => 'preliminaries',
'subcategory' => 'server',
'detail' => 'expected code version',
'err_msg' => 'code version mis-match',
'function' => \&expected_code_version,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a " .
"$default_access_conf --version",
'fatal' => $NO
},
{
'category' => 'preliminaries',
'detail' => 'collecting system specifics',
'err_msg' => 'could not get complete system specs',
'function' => \&specs,
'binary' => $fwknopdCmd,
'fatal' => $NO
},
{
'category' => 'basic operations',
'detail' => 'dump config',
'err_msg' => 'could not dump configuration',
'function' => \&generic_exec,
'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
'exec_err' => $NO,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf " .
"-a $default_access_conf --dump-config",
'fatal' => $NO
},
{
'category' => 'basic operations',
'detail' => 'override config',
'err_msg' => 'could not override configuration',
'function' => \&generic_exec,
'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
'exec_err' => $NO,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args " .
"-O $conf_dir/override_fwknopd.conf --dump-config",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => '--get-key path validation',
'err_msg' => 'accepted improper --get-key path',
'function' => \&generic_exec,
'positive_output_matches' => [qr/could\snot\sopen/i],
'exec_err' => $YES,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -s $fake_ip " .
"-D $loopback_ip --get-key not/there",
'fatal' => $YES
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => 'require [-s|-R|-a]',
'err_msg' => 'allowed null allow IP',
'function' => \&generic_exec,
'positive_output_matches' => [qr/must\suse\sone\sof/i],
'exec_err' => $YES,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -D $loopback_ip",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => '--allow-ip <IP> valid IP',
'err_msg' => 'permitted invalid --allow-ip arg',
'function' => \&generic_exec,
'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
'exec_err' => $YES,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => '-A <proto>/<port> specification',
'err_msg' => 'permitted invalid -A <proto>/<port>',
'function' => \&generic_exec,
'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
'exec_err' => $YES,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => 'generate SPA packet',
'err_msg' => 'could not generate SPA packet',
'function' => \&client_send_spa_packet,
'cmdline' => $default_client_args,
'fatal' => $YES
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'list current fwknopd fw rules',
'err_msg' => 'could not list current fwknopd fw rules',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --fw-list",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'list all current fw rules',
'err_msg' => 'could not list all current fw rules',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --fw-list-all",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'flush current firewall rules',
'err_msg' => 'could not flush current fw rules',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --fw-flush",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'start',
'err_msg' => 'start error',
'function' => \&server_start,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'stop',
'err_msg' => 'stop error',
'function' => \&server_stop,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'write PID',
'err_msg' => 'did not write PID',
'function' => \&write_pid,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => '--packet-limit 1 exit',
'err_msg' => 'did not exit after one packet',
'function' => \&server_packet_limit,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => 'ignore packets < min SPA len (140)',
'err_msg' => 'did not ignore small packets',
'function' => \&server_ignore_small_packets,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'server',
'detail' => '-P bpf filter ignore packet',
'err_msg' => 'filter did not ignore packet',
'function' => \&server_bpf_ignore_packet,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
qq|-P "udp port $non_std_spa_port"|,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'OPEN_PORTS (tcp/22 ssh)',
'err_msg' => "improper OPEN_PORTS result",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'OPEN_PORTS mismatch',
'err_msg' => "SPA packet accepted",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'require user (tcp/22 ssh)',
'err_msg' => "missed require user criteria",
'function' => \&spa_cycle,
'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'user mismatch (tcp/22 ssh)',
'err_msg' => "improper user accepted for access",
'function' => \&user_mismatch,
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'require src (tcp/22 ssh)',
'err_msg' => "fw rule not created",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'mismatch require src (tcp/22 ssh)',
'err_msg' => "fw rule created",
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'IP filtering (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'subnet filtering (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'IP+subnet filtering (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'IP match (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'subnet match (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'multi IP/net match (tcp/22 ssh)',
'err_msg' => "did not filter $loopback_ip",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'multi access stanzas (tcp/22 ssh)',
'err_msg' => "could not complete SPA cycle",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "non-enabled NAT (tcp/22 ssh)",
'err_msg' => "SPA packet not filtered",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -N $internal_nat_host:22",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
'server_conf' => $nat_conf,
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
'err_msg' => "could not complete NAT SPA cycle",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -N $internal_nat_host:22",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $nat_conf,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/23 telnet)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/9418 git)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (udp/53 dns)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "-P bpf SPA over port $non_std_spa_port",
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args --server-port $non_std_spa_port",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str " .
qq|-P "udp port $non_std_spa_port"|,
'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'spoof username (tcp/22)',
'err_msg' => 'could not spoof username',
'function' => \&spoof_username,
'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'replay attack detection',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'server',
'detail' => 'digest cache structure',
'err_msg' => 'improper digest cache structure',
'function' => \&digest_cache_structure,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'non-base64 altered SPA data',
'err_msg' => 'allowed improper SPA data',
'function' => \&altered_non_base64_spa_data,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'base64 altered SPA data',
'err_msg' => 'allowed improper SPA data',
'function' => \&altered_base64_spa_data,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'appended data to SPA pkt',
'err_msg' => 'allowed improper SPA data',
'function' => \&appended_spa_data,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'prepended data to SPA pkt',
'err_msg' => 'allowed improper SPA data',
'function' => \&prepended_spa_data,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'multi gpg-IDs (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopdCmd -c $default_conf " .
"-a $multi_gpg_access_conf $intf_str " .
"-d $default_digest_file -p $default_pid_file",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/23 telnet)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/9418 git)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (udp/53 dns)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'replay attack detection',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'non-base64 altered SPA data',
'err_msg' => 'allowed improper SPA data',
'function' => \&altered_non_base64_spa_data,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'base64 altered SPA data',
'err_msg' => 'allowed improper SPA data',
'function' => \&altered_base64_spa_data,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'appended data to SPA pkt',
'err_msg' => 'allowed improper SPA data',
'function' => \&appended_spa_data,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'prepended data to SPA pkt',
'err_msg' => 'allowed improper SPA data',
'function' => \&prepended_spa_data,
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'spoof username (tcp/22 ssh)',
'err_msg' => 'could not spoof username',
'function' => \&spoof_username,
'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'server',
'detail' => 'digest cache structure',
'err_msg' => 'improper digest cache structure',
'function' => \&digest_cache_structure,
'fatal' => $NO
},
);
my %test_keys = (
'category' => $REQUIRED,
'subcategory' => $OPTIONAL,
'detail' => $REQUIRED,
'function' => $REQUIRED,
'binary' => $OPTIONAL,
'cmdline' => $OPTIONAL,
'fwknopd_cmdline' => $OPTIONAL,
'fatal' => $OPTIONAL,
'exec_err' => $OPTIONAL,
'fw_rule_created' => $OPTIONAL,
'fw_rule_removed' => $OPTIONAL,
'server_conf' => $OPTIONAL,
'positive_output_matches' => $OPTIONAL,
'negative_output_matches' => $OPTIONAL,
'server_positive_output_matches' => $OPTIONAL,
'server_negative_output_matches' => $OPTIONAL,
);
if ($diff_mode) {
&diff_test_results();
exit 0;
}
### make sure everything looks as expected before continuing
&init();
&logr("\n[+] Starting the fwknop test suite...\n\n" .
" args: @args_cp\n\n"
);
### save the results from any previous test suite run
### so that we can potentially compare them with --diff
if ($saved_last_results) {
&logr(" Saved results from previous run " .
"to: ${output_dir}.last/\n\n");
}
### main loop through all of the tests
for my $test_hr (@tests) {
&run_test($test_hr);
}
&logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
copy $logfile, "$output_dir/$logfile" or die $!;
exit 0;
#===================== end main =======================
sub run_test() {
my $test_hr = shift;
my $msg = "[$test_hr->{'category'}]";
$msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
$msg .= " $test_hr->{'detail'}";
return unless &process_include_exclude($msg);
if ($list_mode) {
print $msg, "\n";
return;
}
&dots_print($msg);
$executed++;
$current_test_file = "$output_dir/$executed.test";
$server_test_file = "$output_dir/${executed}_fwknopd.test";
&write_test_file("[+] TEST: $msg\n", $current_test_file);
$test_hr->{'msg'} = $msg;
if (&{$test_hr->{'function'}}($test_hr)) {
&logr("pass ($executed)\n");
$passed++;
} else {
&logr("fail ($executed)\n");
$failed++;
if ($test_hr->{'fatal'} eq $YES) {
die "[*] required test failed, exiting.";
}
}
return;
}
sub process_include_exclude() {
my $msg = shift;
### inclusions/exclusions
if (@tests_to_include) {
my $found = 0;
for my $test (@tests_to_include) {
if ($msg =~ /$test/) {
$found = 1;
last;
}
}
return 0 unless $found;
}
if (@tests_to_exclude) {
my $found = 0;
for my $test (@tests_to_exclude) {
if ($msg =~ /$test/) {
$found = 1;
last;
}
}
return 0 if $found;
}
return 1;
}
sub diff_test_results() {
die "[*] Need results from a previous run before running --diff"
unless -d "${output_dir}.last";
die "[*] Current results set does not exist." unless -d $output_dir;
my %current_tests = ();
my %previous_tests = ();
### Only diff results for matching tests (parse the logfile to see which
### test numbers match across the two test cycles).
&build_results_hash(\%current_tests, $output_dir);
&build_results_hash(\%previous_tests, "${output_dir}.last");
for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
keys %current_tests) {
my $current_result = $current_tests{$test_msg}{'pass_fail'};
my $current_num = $current_tests{$test_msg}{'num'};
if (defined $previous_tests{$test_msg}) {
print "[+] Checking: $test_msg\n";
my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
my $previous_num = $previous_tests{$test_msg}{'num'};
if ($current_result ne $previous_result) {
print " DIFF: **$current_result** $test_msg\n";
}
&diff_results($previous_num, $current_num);
print "\n";
}
}
exit 0;
}
sub diff_results() {
my ($previous_num, $current_num) = @_;
### edit out any valgrind "==354==" prefixes
my $valgrind_search_re = qr/^==\d+==\s/;
### remove CMD timestamps
my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
for my $file ("${output_dir}.last/${previous_num}.test",
"${output_dir}.last/${previous_num}_fwknopd.test",
"${output_dir}/${current_num}.test",
"${output_dir}/${current_num}_fwknopd.test",
) {
system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
}
if (-e "${output_dir}.last/${previous_num}.test"
and -e "${output_dir}/${current_num}.test") {
system "diff -u ${output_dir}.last/${previous_num}.test " .
"${output_dir}/${current_num}.test";
}
if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
and -e "${output_dir}/${current_num}_fwknopd.test") {
system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
"${output_dir}/${current_num}_fwknopd.test";
}
return;
}
sub build_results_hash() {
my ($hr, $dir) = @_;
open F, "< $dir/$logfile" or die $!;
while (<F>) {
if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
$hr->{$1}{'pass_fail'} = $2;
$hr->{$1}{'num'} = $3;
}
}
return;
}
sub compile_warnings() {
### 'make clean' as root
return 0 unless &run_cmd('make -C .. clean',
$cmd_out_tmp, $current_test_file);
if ($sudo_path) {
my $username = getpwuid((stat($configure_path))[4]);
die "[*] Could not determine $configure_path owner"
unless $username;
return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
$cmd_out_tmp, $current_test_file);
} else {
return 0 unless &run_cmd('make -C ..',
$cmd_out_tmp, $current_test_file);
}
### look for compilation warnings - something like:
### warning: ‘test’ is used uninitialized in this function
return 0 if &file_find_regex([qr/\swarning:\s/], $current_test_file);
### the new binaries should exist
unless (-e $fwknopCmd and -x $fwknopCmd) {
&write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
$current_test_file);
}
unless (-e $fwknopdCmd and -x $fwknopdCmd) {
&write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
$current_test_file);
}
return 1;
}
sub binary_exists() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
### account for different libfko.so paths (e.g. libfko.so.0.3 with no
### libfko.so link on OpenBSD)
if ($test_hr->{'binary'} =~ /libfko/) {
unless (-e $test_hr->{'binary'}) {
for my $file (glob("$lib_dir/libfko.so*")) {
if (-e $file and -x $file) {
$test_hr->{'binary'} = $file;
$libfko_bin = $file;
last;
}
}
}
}
return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
return 1;
}
sub expected_code_version() {
my $test_hr = shift;
unless (-e '../VERSION') {
&write_test_file("[-] ../VERSION file does not exist.\n",
$current_test_file);
return 0;
}
open F, '< ../VERSION' or die $!;
my $line = <F>;
close F;
if ($line =~ /(\d.*\d)/) {
my $version = $1;
return 0 unless &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $current_test_file);
return 1 if &file_find_regex([qr/$version/], $current_test_file);
}
return 0;
}
sub client_send_spa_packet() {
my $test_hr = shift;
&write_key('fwknoptest', $local_key_file);
return 0 unless &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $current_test_file);
return 0 unless &file_find_regex([qr/final\spacked/i],
$current_test_file);
return 1;
}
sub spa_cycle() {
my $test_hr = shift;
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, [], $USE_CLIENT);
if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
$rv = 0 unless $fw_rule_created;
} elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
$rv = 0 if $fw_rule_created;
}
if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
$rv = 0 unless $fw_rule_removed;
} elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
$rv = 0 if $fw_rule_removed;
}
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$server_test_file);
}
if ($test_hr->{'server_negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'server_negative_output_matches'},
$server_test_file);
}
return $rv;
}
sub spoof_username() {
my $test_hr = shift;
my $rv = &spa_cycle($test_hr);
unless (&file_find_regex([qr/Username:\s*$spoof_user/],
$current_test_file)) {
$rv = 0;
}
unless (&file_find_regex([qr/Username:\s*$spoof_user/],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub replay_detection() {
my $test_hr = shift;
### do a complete SPA cycle and then parse the SPA packet out of the
### current test file and re-send
return 0 unless &spa_cycle($test_hr);
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n",
$current_test_file);
return 0;
}
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub digest_cache_structure() {
my $test_hr = shift;
my $rv = 1;
&run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
### the format should be:
### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
open F, "< $default_digest_file" or
die "[*] could not open $default_digest_file: $!";
while (<F>) {
next if /^#/;
next unless /\S/;
unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
&write_test_file("[-] invalid digest.cache line: $_",
$current_test_file);
$rv = 0;
last;
}
}
close F;
} elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
&write_test_file("[+] DBM digest file format, " .
"assuming this is valid.\n", $current_test_file);
} else {
### don't know what kind of file the digest.cache is
&write_test_file("[-] unrecognized file type for " .
"$default_digest_file.\n", $current_test_file);
$rv = 0;
}
if ($rv) {
&write_test_file("[+] valid digest.cache structure.\n",
$current_test_file);
}
return $rv;
}
sub server_bpf_ignore_packet() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n", $current_test_file);
return 0;
}
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub altered_non_base64_spa_data() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n", $current_test_file);
return 0;
}
### alter one byte (change to a ":")
$spa_pkt =~ s|^(.{3}).|$1:|;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
return $rv;
}
sub altered_base64_spa_data() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n", $current_test_file);
return 0;
}
$spa_pkt =~ s|^(.{3}).|AAAA|;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
if ($fw_rule_created) {
&write_test_file("[-] new fw rule created.\n", $current_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $current_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub appended_spa_data() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n", $current_test_file);
return 0;
}
$spa_pkt .= 'AAAA';
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
if ($fw_rule_created) {
&write_test_file("[-] new fw rule created.\n", $current_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $current_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub prepended_spa_data() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($current_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $current_test_file\n", $current_test_file);
return 0;
}
$spa_pkt = 'AAAA' . $spa_pkt;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
if ($fw_rule_created) {
&write_test_file("[-] new fw rule created.\n", $current_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $current_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub server_start() {
my $test_hr = shift;
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
$server_test_file)) {
$rv = 0;
}
$rv = 0 unless $server_was_stopped;
return $rv;
}
sub server_stop() {
my $test_hr = shift;
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
return $rv;
}
sub server_packet_limit() {
my $test_hr = shift;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => 'A'x700,
},
);
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
if (&is_fwknopd_running()) {
&stop_fwknopd();
$rv = 0;
}
unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
$server_test_file)) {
$rv = 0;
}
unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
$server_test_file)) {
$rv = 0;
}
return $rv;
}
sub server_ignore_small_packets() {
my $test_hr = shift;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
},
);
my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
sleep 2;
if (&is_fwknopd_running()) {
&stop_fwknopd();
$rv = 0;
}
return $rv;
}
sub client_server_interaction() {
my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
my $rv = 1;
my $server_was_stopped = 1;
my $fw_rule_created = 1;
my $fw_rule_removed = 0;
### start fwknopd to monitor for the SPA packet over the loopback interface
my $fwknopd_parent_pid = &start_fwknopd($test_hr);
### give fwknopd a chance to parse its config and start sniffing
### on the loopback interface
if ($use_valgrind) {
sleep 3;
} else {
sleep 2;
}
### send the SPA packet(s) to the server either manually using IO::Socket or
### with the fwknopd client
if ($spa_client_flag == $USE_CLIENT) {
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$current_test_file);
$rv = 0;
}
} else {
&send_packets($pkts_hr);
}
### check to see if the SPA packet resulted in a new fw access rule
my $ctr = 0;
while (not &is_fw_rule_active($test_hr)) {
&write_test_file("[-] new fw rule does not exist.\n",
$current_test_file);
$ctr++;
last if $ctr == 3;
sleep 1;
}
if ($ctr == 3) {
$fw_rule_created = 0;
$fw_rule_removed = 0;
}
&time_for_valgrind() if $use_valgrind;
if ($fw_rule_created) {
sleep 3; ### allow time for rule time out.
if (&is_fw_rule_active($test_hr)) {
&write_test_file("[-] new fw rule not timed out.\n",
$current_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule timed out.\n",
$current_test_file);
$fw_rule_removed = 1;
}
}
if (&is_fwknopd_running()) {
&stop_fwknopd();
unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
$server_test_file)) {
$server_was_stopped = 0;
}
} else {
&write_test_file("[-] server is not running.\n",
$current_test_file);
$server_was_stopped = 0;
}
return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
}
sub get_spa_packet_from_file() {
my $file = shift;
my $spa_pkt = '';
my $found_trigger_line = 0;
open F, "< $file" or die "[*] Could not open file $file: $!";
while (<F>) {
if (/final\spacked/i) {
$found_trigger_line = 1;
next;
}
next unless $found_trigger_line;
### the next line with non whitespace is the SPA packet
if (/(\S+)/) {
$spa_pkt = $1;
last;
}
}
close F;
return $spa_pkt;
}
sub send_packets() {
my $pkts_ar = shift;
open F, ">> $current_test_file" or die $!;
print F "[+] send_packets(): Sending the following packets...\n";
print F Dumper $pkts_ar;
close F;
for my $pkt_hr (@$pkts_ar) {
if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
my $socket = IO::Socket::INET->new(
PeerAddr => $pkt_hr->{'dst_ip'},
PeerPort => $pkt_hr->{'port'},
Proto => $pkt_hr->{'proto'},
Timeout => 1
) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
"socket to $pkt_hr->{'dst_ip'}: $!";
$socket->send($pkt_hr->{'data'});
undef $socket;
} elsif ($pkt_hr->{'proto'} eq 'http') {
### FIXME
} elsif ($pkt_hr->{'proto'} eq 'icmp') {
### FIXME
}
sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
}
return;
}
sub generic_exec() {
my $test_hr = shift;
my $rv = 1;
my $exec_rv = &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $current_test_file);
if ($test_hr->{'exec_err'} eq $YES) {
$rv = 0 if $exec_rv;
} else {
$rv = 0 unless $exec_rv;
}
if ($test_hr->{'positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'positive_output_matches'},
$current_test_file);
}
if ($test_hr->{'negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'negative_output_matches'},
$current_test_file);
}
return $rv;
}
### check for PIE
sub pie_binary() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
$current_test_file);
return 1;
}
### check for stack protection
sub stack_protected_binary() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
$current_test_file);
return 1;
}
### check for fortified source functions
sub fortify_source_functions() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
$current_test_file);
return 1;
}
### check for read-only relocations
sub read_only_relocations() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
$current_test_file);
return 1;
}
### check for immediate binding
sub immediate_binding() {
my $test_hr = shift;
return 0 unless $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
$current_test_file);
return 1;
}
sub specs() {
&run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
"$default_server_conf_args --fw-list-all",
$cmd_out_tmp, $current_test_file);
my $have_gpgme = 0;
for my $cmd (
'uname -a',
'uptime',
'ifconfig -a',
'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
'if [ `which iptables` ]; then iptables -V; fi',
'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
'if [ `which gpg` ]; then gpg --version; fi',
'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
"ldd $fwknopCmd",
"ldd $fwknopdCmd",
"ldd $libfko_bin",
'ls -l /usr/lib/*pcap*',
'ls -l /usr/local/lib/*pcap*',
'ls -l /usr/lib/*fko*',
'ls -l /usr/local/lib/*fko*',
) {
&run_cmd($cmd, $cmd_out_tmp, $current_test_file);
if ($cmd =~ /^ldd/) {
$have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
}
}
### all three of fwknop/fwknopd/libfko must link against gpgme in order
### to enable gpg tests
unless ($have_gpgme == 3) {
push @tests_to_exclude, "GPG";
}
return 1;
}
sub time_for_valgrind() {
my $ctr = 0;
while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
"grep valgrind |grep -v perl | grep -v grep",
$cmd_out_tmp, $current_test_file)) {
$ctr++;
last if $ctr == 5;
sleep 1;
}
return;
}
sub anonymize_results() {
my $rv = 0;
die "[*] $output_dir does not exist" unless -d $output_dir;
die "[*] $logfile does not exist, has $0 been executed?"
unless -e $logfile;
if (-e $tarfile) {
unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
}
### remove non-loopback IP addresses
my $search_re = qr/\b127\.0\.0\.1\b/;
system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
$search_re = qr/\b127\.0\.0\.2\b/;
system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
$search_re = qr/\b0\.0\.0\.0\b/;
system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
$search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
### remove hostname from any uname output
$search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
system "perl -p -i -e 'undef \$/; s|$search_re" .
"| uname -a\n\$1 (removed)|s' $output_dir/*.test";
$search_re = qr/uname=\x27(\S+)\s+\S+/;
system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
### create tarball
system "tar cvfz $tarfile $logfile $output_dir";
print "[+] Anonymized test results file: $tarfile\n";
if (-e $tarfile) {
$rv = 1;
}
return $rv;
}
sub write_pid() {
my $test_hr = shift;
open F, "> $default_pid_file" or die $!;
print F "1\n";
close F;
&server_start($test_hr);
open F, "< $default_pid_file" or die $!;
my $pid = <F>;
chomp $pid;
close F;
if ($pid != 1) {
return 1;
}
return 0;
}
sub start_fwknopd() {
my $test_hr = shift;
&write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
my $pid = fork();
die "[*] Could not fork: $!" unless defined $pid;
if ($pid == 0) {
### we are the child, so start fwknopd
exit &run_cmd($test_hr->{'fwknopd_cmdline'},
$server_cmd_tmp, $server_test_file);
}
return $pid;
}
sub write_key() {
my ($key, $file) = @_;
open K, "> $file" or die "[*] Could not open $file: $!";
print K "$loopback_ip: $key\n";
print K "localhost: $key\n";
print K "some.host.through.proxy.com: $key\n";
close K;
return;
}
sub dump_pids() {
open C, ">> $current_test_file"
or die "[*] Could not open $current_test_file: $!";
print C "\n" . localtime() . " [+] PID dump:\n";
close C;
&run_cmd("ps auxww | grep knop |grep -v grep",
$cmd_out_tmp, $current_test_file);
return;
}
sub run_cmd() {
my ($cmd, $cmd_out, $file) = @_;
if (-e $file) {
open F, ">> $file"
or die "[*] Could not open $file: $!";
print F localtime() . " CMD: $cmd\n";
close F;
} else {
open F, "> $file"
or die "[*] Could not open $file: $!";
print F localtime() . " CMD: $cmd\n";
close F;
}
my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
my @cmd_lines = <C>;
close C;
open F, ">> $file" or die "[*] Could not open $file: $!";
print F $_ for @cmd_lines;
close F;
if ($rv == 0) {
return 1;
}
return 0;
}
sub dots_print() {
my $msg = shift;
&logr($msg);
my $dots = '';
for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
$dots .= '.';
}
&logr($dots);
return;
}
sub init() {
$|++; ### turn off buffering
$< == 0 && $> == 0 or
die "[*] $0: You must be root (or equivalent ",
"UID 0 account) to effectively test fwknop";
### validate test hashes
my $hash_num = 0;
for my $test_hr (@tests) {
for my $key (keys %test_keys) {
if ($test_keys{$key} == $REQUIRED) {
die "[*] Missing '$key' element in hash: $hash_num"
unless defined $test_hr->{$key};
} else {
$test_hr->{$key} = '' unless defined $test_hr->{$key};
}
}
$hash_num++;
}
if ($use_valgrind) {
die "[*] $valgrindCmd exec problem, use --valgrind-path"
unless -e $valgrindCmd and -x $valgrindCmd;
}
die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
for my $file ($configure_path,
$default_conf,
$nat_conf,
$default_access_conf,
$no_source_match_access_conf,
$ip_source_match_access_conf,
$subnet_source_match_access_conf,
$no_subnet_source_match_access_conf,
$no_multi_source_match_access_conf,
$multi_source_match_access_conf,
$open_ports_access_conf,
$mismatch_open_ports_access_conf,
$require_user_access_conf,
$mismatch_user_access_conf,
$require_src_access_conf,
$multi_gpg_access_conf,
$multi_stanzas_access_conf,
) {
die "[*] $file does not exist" unless -e $file;
}
if (-d $output_dir) {
if (-d "${output_dir}.last") {
rmtree "${output_dir}.last"
or die "[*] rmtree ${output_dir}.last $!";
}
mkdir "${output_dir}.last"
or die "[*] ${output_dir}.last: $!";
for my $file (glob("$output_dir/*.test")) {
if ($file =~ m|.*/(.*)|) {
copy $file, "${output_dir}.last/$1" or die $!;
}
}
if (-e "$output_dir/init") {
copy "$output_dir/init", "${output_dir}.last/init";
}
if (-e $logfile) {
copy $logfile, "${output_dir}.last/$logfile" or die $!;
}
$saved_last_results = 1;
} else {
mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
}
unless (-d $run_dir) {
mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
}
for my $file (glob("$output_dir/*.test")) {
unlink $file or die "[*] Could not unlink($file)";
}
if (-e "$output_dir/init") {
unlink "$output_dir/init" or die $!;
}
if (-e $logfile) {
unlink $logfile or die $!;
}
if ($test_include) {
@tests_to_include = split /\s*,\s*/, $test_include;
}
if ($test_exclude) {
@tests_to_exclude = split /\s*,\s*/, $test_exclude;
}
### make sure no fwknopd instance is currently running
die "[*] Please stop the running fwknopd instance."
if &is_fwknopd_running();
unless ($enable_recompilation_warnings_check) {
push @tests_to_exclude, 'recompilation';
}
$sudo_path = &find_command('sudo');
unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
### disable compilation checks
push @tests_to_exclude, 'recompilation';
}
open UNAME, "uname |" or die "[*] Could not execute uname: $!";
while (<UNAME>) {
if (/linux/i) {
$platform = 'linux';
last;
}
}
close UNAME;
unless ($platform eq 'linux') {
push @tests_to_exclude, 'NAT';
}
return;
}
sub identify_loopback_intf() {
return if $loopback_intf;
### Linux:
### lo Link encap:Local Loopback
### inet addr:127.0.0.1 Mask:255.0.0.0
### inet6 addr: ::1/128 Scope:Host
### UP LOOPBACK RUNNING MTU:16436 Metric:1
### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
### collisions:0 txqueuelen:0
### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
### Freebsd:
### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
### options=3<RXCSUM,TXCSUM>
### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
### inet6 ::1 prefixlen 128
### inet 127.0.0.1 netmask 0xff000000
### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
my $intf = '';
my $found_loopback_intf = 0;
my $cmd = 'ifconfig -a';
open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
while (<C>) {
if (/^(\S+?):?\s+.*loopback/i) {
$intf = $1;
next;
}
if (/^\S/ and $intf and not $found_loopback_intf) {
### should not happen
last;
}
if ($intf and /\b127\.0\.0\.1\b/) {
$found_loopback_intf = 1;
last;
}
}
close C;
die "[*] could not determine loopback interface, use --loopback <name>"
unless $found_loopback_intf;
$loopback_intf = $intf;
return;
}
sub is_fw_rule_active() {
my $test_hr = shift;
my $conf_args = $default_server_conf_args;
if ($test_hr->{'server_conf'}) {
$conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
"-d $default_digest_file -p $default_pid_file";
}
return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
$cmd_out_tmp, $current_test_file);
return 0;
}
sub is_fwknopd_running() {
sleep 2 if $use_valgrind;
&run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
"--status", $cmd_out_tmp, $current_test_file);
return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
return 1;
}
sub stop_fwknopd() {
&run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
"$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
if ($use_valgrind) {
&time_for_valgrind();
} else {
sleep 1;
}
return;
}
sub file_find_regex() {
my ($re_ar, $file) = @_;
my $found = 0;
my @write_lines = ();
open F, "< $file" or die "[*] Could not open $file: $!";
LINE: while (<F>) {
my $line = $_;
next LINE if $line =~ /file_file_regex\(\)/;
for my $re (@$re_ar) {
if ($line =~ $re) {
push @write_lines, "[.] file_find_regex() " .
"Matched '$re' with line: $line";
$found = 1;
last LINE;
}
}
}
close F;
if ($found) {
for my $line (@write_lines) {
&write_test_file($line, $file);
}
} else {
&write_test_file("[.] find_find_regex() Did not " .
"match any regex in: '@$re_ar'\n", $file);
}
return $found;
}
sub find_command() {
my $cmd = shift;
my $path = '';
open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
while (<C>) {
if (m|^(/.*$cmd)$|) {
$path = $1;
last;
}
}
close C;
return $path;
}
sub write_test_file() {
my ($msg, $file) = @_;
if (-e $file) {
open F, ">> $file"
or die "[*] Could not open $file: $!";
print F $msg;
close F;
} else {
open F, "> $file"
or die "[*] Could not open $file: $!";
print F $msg;
close F;
}
return;
}
sub logr() {
my $msg = shift;
print STDOUT $msg;
open F, ">> $logfile" or die $!;
print F $msg;
close F;
return;
}
Jump to Line
Something went wrong with that request. Please try again.