Files
fwknop/test/test-fwknop.pl
Michael Rash b820bbbe4b Minor memory leak bug fix in --rotate-digest-cache mode
This commit fixes a minor memory leak for the digest cache file path in
--rotate-digest-cache mode in the replay_cache_init() function.  The leak was
caught by valgrind, and a new test was added to the test suite for it.  Here
is the valgrind warning:

==29021== 21 bytes in 1 blocks are definitely lost in loss record 2 of 2
==29021==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29021==    by 0x1103AA: replay_cache_init (replay_cache.c:96)
==29021==    by 0x10BB8C: main (fwknopd.c:254)
2013-02-10 14:57:44 -05:00

7127 lines
243 KiB
Perl
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/perl -w
use Cwd;
use File::Copy;
use File::Path;
use IO::Socket;
use Data::Dumper;
use Cwd;
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 $data_tmp = 'data.tmp';
my $key_tmp = 'key.tmp';
my $enc_save_tmp = 'openssl_save.enc';
my $gpg_client_home_dir = "$conf_dir/client-gpg";
my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw";
my $replay_pcap_file = "$conf_dir/spa_replay.pcap";
my %cf = (
'nat' => "$conf_dir/nat_fwknopd.conf",
'def' => "$conf_dir/default_fwknopd.conf",
'def_access' => "$conf_dir/default_access.conf",
'hmac_access' => "$conf_dir/hmac_access.conf",
'exp_access' => "$conf_dir/expired_stanza_access.conf",
'future_exp_access' => "$conf_dir/future_expired_stanza_access.conf",
'exp_epoch_access' => "$conf_dir/expired_epoch_stanza_access.conf",
'invalid_exp_access' => "$conf_dir/invalid_expire_access.conf",
'force_nat_access' => "$conf_dir/force_nat_access.conf",
'cmd_access' => "$conf_dir/cmd_access.conf",
'local_nat' => "$conf_dir/local_nat_fwknopd.conf",
'ipfw_active_expire' => "$conf_dir/ipfw_active_expire_equal_fwknopd.conf",
'android_access' => "$conf_dir/android_access.conf",
'android_legacy_iv_access' => "$conf_dir/android_legacy_iv_access.conf",
'dual_key_access' => "$conf_dir/dual_key_usage_access.conf",
'gpg_access' => "$conf_dir/gpg_access.conf",
'legacy_iv_access' => "$conf_dir/legacy_iv_access.conf",
'gpg_no_pw_access' => "$conf_dir/gpg_no_pw_access.conf",
'tcp_server' => "$conf_dir/tcp_server_fwknopd.conf",
'tcp_pcap_filter' => "$conf_dir/tcp_pcap_filter_fwknopd.conf",
'icmp_pcap_filter' => "$conf_dir/icmp_pcap_filter_fwknopd.conf",
'open_ports_access' => "$conf_dir/open_ports_access.conf",
'multi_gpg_access' => "$conf_dir/multi_gpg_access.conf",
'multi_gpg_no_pw_access' => "$conf_dir/multi_gpg_no_pw_access.conf",
'multi_stanza_access' => "$conf_dir/multi_stanzas_access.conf",
'broken_keys_access' => "$conf_dir/multi_stanzas_with_broken_keys.conf",
'ecb_mode_access' => "$conf_dir/ecb_mode_access.conf",
'ctr_mode_access' => "$conf_dir/ctr_mode_access.conf",
'cfb_mode_access' => "$conf_dir/cfb_mode_access.conf",
'ofb_mode_access' => "$conf_dir/ofb_mode_access.conf",
'open_ports_mismatch' => "$conf_dir/mismatch_open_ports_access.conf",
'require_user_access' => "$conf_dir/require_user_access.conf",
'user_mismatch_access' => "$conf_dir/mismatch_user_access.conf",
'require_src_access' => "$conf_dir/require_src_access.conf",
'invalid_src_access' => "$conf_dir/invalid_source_access.conf",
'no_src_match' => "$conf_dir/no_source_match_access.conf",
'no_subnet_match' => "$conf_dir/no_subnet_source_match_access.conf",
'no_multi_src' => "$conf_dir/no_multi_source_match_access.conf",
'multi_src_access' => "$conf_dir/multi_source_match_access.conf",
'ip_src_match' => "$conf_dir/ip_source_match_access.conf",
'subnet_src_match' => "$conf_dir/ip_source_match_access.conf",
'rc_file_def_key' => "$conf_dir/fwknoprc_with_default_key",
'rc_file_def_b64_key' => "$conf_dir/fwknoprc_with_default_base64_key",
'rc_file_named_key' => "$conf_dir/fwknoprc_named_key",
'rc_file_invalid_b64_key' => "$conf_dir/fwknoprc_invalid_base64_key",
'rc_file_hmac_b64_key' => "$conf_dir/fwknoprc_default_hmac_base64_key",
'base64_key_access' => "$conf_dir/base64_key_access.conf",
'disable_aging' => "$conf_dir/disable_aging_fwknopd.conf",
'disable_aging_nat' => "$conf_dir/disable_aging_nat_fwknopd.conf",
'fuzz_source' => "$conf_dir/fuzzing_source_access.conf",
'fuzz_open_ports' => "$conf_dir/fuzzing_open_ports_access.conf",
'fuzz_restrict_ports' => "$conf_dir/fuzzing_restrict_ports_access.conf",
);
my $default_digest_file = "$run_dir/digest.cache";
my $default_pid_file = "$run_dir/fwknopd.pid";
my $tmp_rc_file = "$run_dir/fwknoprc";
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 $force_nat_host = '192.168.1.123';
my $default_spa_port = 62201;
my $non_std_spa_port = 12345;
my $spoof_user = 'testuser';
my $valgrind_cov_dir = 'valgrind-coverage';
my $spoof_ip = '1.2.3.4';
my $perl_mod_fko_dir = 'FKO';
my $cmd_exec_test_file = '/tmp/fwknoptest';
my $default_key = 'fwknoptest';
#================== 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 %valgrind_flagged_fcns = ();
my %valgrind_flagged_fcns_unique = ();
my $previous_valgrind_coverage_dir = '';
my $uniq_keys = 100;
my $test_limit = 0;
my $list_mode = 0;
my $diff_dir1 = '';
my $diff_dir2 = '';
my $loopback_intf = '';
my $anonymize_results = 0;
my $curr_test_file = "$output_dir/init";
my $tarfile = 'test_fwknop.tar.gz';
my $key_gen_file = "$output_dir/key_gen";
my $fuzzing_pkts_file = 'fuzzing/fuzzing_spa_packets';
my $fuzzing_pkts_append = 0;
my $fuzzing_key = 'testtest';
my $fuzzing_num_pkts = 0;
my $fuzzing_test_tag = '';
my $fuzzing_class = 'bogus data';
my %fuzzing_spa_packets = ();
my $total_fuzzing_pkts = 0;
my $server_test_file = '';
my $use_valgrind = 0;
my $valgrind_str = '';
my %prev_valgrind_cov = ();
my $enable_client_ip_resolve_test = 0;
my $enable_all = 0;
my $saved_last_results = 0;
my $diff_mode = 0;
my $fko_obj = ();
my $enable_recompilation_warnings_check = 0;
my $enable_profile_coverage_check = 0;
my $enable_make_distcheck = 0;
my $enable_perl_module_checks = 0;
my $enable_perl_module_fuzzing_spa_pkt_generation = 0;
my $enable_openssl_compatibility_tests = 0;
my $openssl_success_ctr = 0;
my $openssl_failure_ctr = 0;
my $openssl_ctr = 0;
my $fuzzing_success_ctr = 0;
my $fuzzing_failure_ctr = 0;
my $fuzzing_ctr = 0;
my $sudo_path = '';
my $gcov_path = '';
my $killall_path = '';
my $openssl_path = '';
my $pinentry_fail = 0;
my $platform = '';
my $help = 0;
my $YES = 1;
my $NO = 0;
my $IGNORE = 2;
my $PRINT_LEN = 68;
my $USE_PREDEF_PKTS = 1;
my $USE_CLIENT = 2;
my $USE_PCAP_FILE = 3;
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 $MATCH_ANY = 1;
my $MATCH_ALL = 2;
my $REQUIRE_SUCCESS = 0;
my $REQUIRE_FAILURE = 1;
my $LINUX = 1;
my $FREEBSD = 2;
my $MACOSX = 3;
my $OPENBSD = 4;
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-perl-module-checks' => \$enable_perl_module_checks,
'enable-perl-module-pkt-generation' => \$enable_perl_module_fuzzing_spa_pkt_generation,
'fuzzing-pkts-file=s' => \$fuzzing_pkts_file,
'fuzzing-pkts-append' => \$fuzzing_pkts_append,
'fuzzing-test-tag=s' => \$fuzzing_test_tag,
'fuzzing-class=s' => \$fuzzing_class,
'enable-recompile-check' => \$enable_recompilation_warnings_check,
'enable-profile-coverage-check' => \$enable_profile_coverage_check,
'enable-ip-resolve' => \$enable_client_ip_resolve_test,
'enable-distcheck' => \$enable_make_distcheck,
'enable-dist-check' => \$enable_make_distcheck, ### synonym
'enable-openssl-checks' => \$enable_openssl_compatibility_tests,
'List-mode' => \$list_mode,
'test-limit=i' => \$test_limit,
'enable-valgrind' => \$use_valgrind,
'enable-all' => \$enable_all,
'valgrind-path=s' => \$valgrindCmd,
### can set the following to "output.last/valgrind-coverage" if
### a full test suite run has already been executed with --enable-valgrind
'valgrind-prev-cov-dir=s' => \$previous_valgrind_coverage_dir,
'output-dir=s' => \$output_dir,
'diff' => \$diff_mode,
'diff-dir1=s' => \$diff_dir1,
'diff-dir2=s' => \$diff_dir2,
'help' => \$help
);
&usage() if $help;
if ($enable_all) {
$use_valgrind = 1;
$enable_recompilation_warnings_check = 1;
$enable_client_ip_resolve_test = 1;
$enable_make_distcheck = 1;
}
### 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 --no-save-args --verbose --verbose";
my $default_client_args_no_get_key = "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip " .
"--no-save-args --verbose --verbose";
my $client_ip_resolve_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22 -R -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_client_gpg_args_no_homedir = "$default_client_args " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key ";
my $default_client_gpg_args_no_get_key = "$default_client_args_no_get_key " .
"--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 $cf{'def'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file";
my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopdCmd -c $cf{'def'} " .
"-a $cf{'gpg_access'} $intf_str " .
"-d $default_digest_file -p $default_pid_file";
my $default_server_gpg_args_no_pw = "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopdCmd -c $cf{'def'} " .
"-a $cf{'gpg_no_pw_access'} $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' => 'make distcheck',
'detail' => 'ensure proper distribution creation',
'err_msg' => 'could not create proper tarball',
'function' => \&make_distcheck,
'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' => '--test mode, packet not sent',
'err_msg' => '--test mode, packet sent?',
'function' => \&generic_exec,
'positive_output_matches' => [qr/test\smode\senabled/],
'cmdline' => "$default_client_args --test",
'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 $cf{'def'} -a " .
"$cf{'def_access'} --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 $cf{'def'} " .
"-a $cf{'def_access'} --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' => 'show last args',
'err_msg' => 'could not show last args',
'function' => \&generic_exec,
'positive_output_matches' => [qr/Could\snot|Last\sfwknop/i],
'exec_err' => $IGNORE,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd --show-last",
'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 -a $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 (proto)',
'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' => '-A <proto>/<port> specification (port)',
'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 tcp/600001 -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' => 'rotate digest file',
'err_msg' => 'could not rotate digest file',
'function' => \&rotate_digest_file,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str --rotate-digest-cache",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'permissions check cycle (tcp/22)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&permissions_check,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_positive_output_matches' => [qr/permissions\sshould\sonly\sbe\suser/],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'client IP resolve (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => $client_ip_resolve_args,
'no_ip_check' => 1,
'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 MD5 (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -m md5",
'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 SHA1 (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -m sha1",
'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 SHA256 (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -m sha256",
'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 SHA384 (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -m sha384",
'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 SHA512 (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -m sha512",
'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',
'detail' => 'validate digest type arg',
'err_msg' => 'could not complete SPA cycle',
'function' => \&generic_exec,
'cmdline' => "$default_client_args -m invaliddigest",
'positive_output_matches' => [qr/Invalid\sdigest\stype/i],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'dual usage access key (tcp/80 http)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'dual_key_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
### check for the first stanza that does not allow tcp/80 - the
### second stanza allows this
'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'create rc file (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args --rc-file $tmp_rc_file",
'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,
'key_file' => $tmp_rc_file,
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => "rc file created",
'err_msg' => "rc file $tmp_rc_file does not exist",
'function' => \&rc_file_exists,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'rc file default key (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_def_key'}",
'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,
'key_file' => $cf{'rc_file_def_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'rc file base64 key (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_def_b64_key'}",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'base64_key_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'key_file' => $cf{'rc_file_def_b64_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'rc file named key (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_named_key'} -n testssh",
'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,
'key_file' => $cf{'rc_file_named_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => 'rc file HMAC base64 key (tcp/22 ssh)',
'err_msg' => 'SPA packet not generated',
'function' => \&generic_exec,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_hmac_b64_key'}",
'key_file' => $cf{'rc_file_hmac_b64_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle + HMAC (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_hmac_b64_key'}",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_access'} " .
"-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' => 'altered HMAC (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&altered_hmac_spa_data, ### alter HMAC itself
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_hmac_b64_key'}",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'key_file' => $cf{'rc_file_hmac_b64_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'altered pkt HMAC (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&altered_pkt_hmac_spa_data, ### alter SPA payload
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_hmac_b64_key'}",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'key_file' => $cf{'rc_file_hmac_b64_key'},
'fatal' => $NO
},
### --key-gen tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => '--key-gen',
'err_msg' => 'keys not generated',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopCmd --key-gen",
'positive_output_matches' => [qr/BASE64/, qw/HMAC/, qw/KEY/],
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => "--key-gen $uniq_keys key uniqueness",
'err_msg' => 'keys not generated',
'function' => \&key_gen_uniqueness,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
"$fwknopCmd --key-gen", ### no valgrind string (too slow for 100 client exec's)
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => '--key-gen to file',
'err_msg' => 'keys not generated',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopCmd --key-gen --key-gen-file $key_gen_file",
'positive_output_matches' => [qr/Wrote.*\skeys/],
'fatal' => $NO
},
### rc file tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => 'rc file invalid stanza (tcp/22 ssh)',
'err_msg' => 'SPA packet generated/accepted',
'function' => \&generic_exec,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_named_key'} -n invalidstanza",
'positive_output_matches' => [qr/Named\sconfiguration.*not\sfound/],
'key_file' => $cf{'rc_file_named_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => 'rc file invalid base64 key (tcp/22 ssh)',
'err_msg' => 'SPA packet generated/accepted',
'function' => \&generic_exec,
'cmdline' => "$default_client_args_no_get_key " .
"--rc-file $cf{'rc_file_invalid_b64_key'} -n testssh",
'positive_output_matches' => [qr/look\slike\sbase64\-encoded/],
'key_file' => $cf{'rc_file_invalide_b64_key'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'packet aging (past) (tcp/22 ssh)',
'err_msg' => 'old SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args --time-offset-minus 300s",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'packet aging (future) (tcp/22 ssh)',
'err_msg' => 'future SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args --time-offset-plus 300s",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'invalid SOURCE (tcp/22 ssh)',
'err_msg' => 'SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'invalid_src_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Fatal\serror\sparsing\sIP\sto\sint/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'expired stanza (tcp/22 ssh)',
'err_msg' => 'SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'exp_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'invalid expire date (tcp/22 ssh)',
'err_msg' => 'SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'invalid_exp_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'expired epoch stanza (tcp/22 ssh)',
'err_msg' => 'SPA packet accepted',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'exp_epoch_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'future expired stanza (tcp/22 ssh)',
'err_msg' => 'SPA packet not accepted',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'future_exp_access'} " .
"-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 (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 $cf{'def'} -a $cf{'open_ports_access'} " .
"-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 $cf{'def'} -a $cf{'open_ports_mismatch'} " .
"-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
},
### spoof the source IP on the SPA packet
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "udpraw spoof src IP (tcp/22 ssh)",
'err_msg' => "could not spoof source IP",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -P udpraw -Q $spoof_ip",
'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,
'server_positive_output_matches' => [qr/SPA\sPacket\sfrom\sIP\:\s$spoof_ip\s/],
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "tcpraw spoof src IP (tcp/22 ssh)",
'err_msg' => "could not spoof source IP",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -P tcpraw -Q $spoof_ip",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'tcp_pcap_filter'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_positive_output_matches' => [qr/SPA\sPacket\sfrom\sIP\:\s$spoof_ip\s/],
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "icmp spoof src IP (tcp/22 ssh)",
'err_msg' => "could not spoof source IP",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -P icmp -Q $spoof_ip",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'icmp_pcap_filter'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_positive_output_matches' => [qr/SPA\sPacket\sfrom\sIP\:\s$spoof_ip\s/],
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "icmp type/code 8/0 spoof src IP ",
'err_msg' => "could not spoof source IP",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -P icmp --icmp-type 8 --icmp-code 0 -Q $spoof_ip",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'icmp_pcap_filter'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_positive_output_matches' => [qr/SPA\sPacket\sfrom\sIP\:\s$spoof_ip\s/],
'fatal' => $NO
},
### SPA over TCP (not really "single" packet auth since a TCP connection
### is established)
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "SPA over TCP connection",
'err_msg' => "could not send/process SPA packet over TCP connection",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -P tcp",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'tcp_server'} -a $cf{'def_access'} " .
"-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' => '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 $cf{'def'} -a $cf{'require_user_access'} " .
"-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 $cf{'def'} -a $cf{'user_mismatch_access'} " .
"-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 $cf{'def'} -a $cf{'require_src_access'} " .
"-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 $cf{'def'} -a $cf{'require_src_access'} " .
"-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' => 'allow -s (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'no_ip_check' => 1,
'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 $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' => '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 $cf{'def'} -a $cf{'no_src_match'} " .
"-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 $cf{'def'} -a $cf{'no_subnet_match'} " .
"-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 $cf{'def'} -a $cf{'no_multi_src'} " .
"-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 $cf{'def'} -a $cf{'ip_src_match'} " .
"-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 $cf{'def'} -a $cf{'subnet_src_match'} " .
"-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 $cf{'def'} -a $cf{'multi_src_access'} " .
"-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 $cf{'def'} -a $cf{'multi_stanza_access'} " .
"-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' => 'bad/good key 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 $cf{'def'} -a $cf{'broken_keys_access'} " .
"-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' => $cf{'nat'},
'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 $cf{'nat'} -a $cf{'open_ports_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client',
'detail' => "NAT bogus IP validation",
'err_msg' => "could not complete NAT SPA cycle",
'function' => \&generic_exec,
'exec_err' => $YES,
'cmdline' => "$default_client_args -N 999.1.1.1:22",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
'err_msg' => "could not complete NAT SPA cycle",
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'nat'} -a $cf{'force_nat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/\sto\:$force_nat_host\:22/i],
'server_negative_output_matches' => [qr/\sto\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "local NAT $force_nat_host (tcp/22 ssh)",
'err_msg' => "could not complete NAT SPA cycle",
'function' => \&spa_cycle,
'cmdline' => "$default_client_args --nat-local",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'local_nat'} -a $cf{'force_nat_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i,
qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => "local NAT non-FORCE_NAT (tcp/22 ssh)",
'err_msg' => "could not complete NAT SPA cycle",
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose --nat-local --nat-port 22",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'local_nat'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/to\:$loopback_ip\:22/i,
qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'server_conf' => $cf{'nat'},
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'ECB mode (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -M ecb",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'ecb_mode_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_negative_output_matches' => [qr/Decryption\sfailed/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'CFB mode (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -M cfb",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'cfb_mode_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_negative_output_matches' => [qr/Decryption\sfailed/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'CTR mode (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -M ctr",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'ctr_mode_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_negative_output_matches' => [qr/Decryption\sfailed/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'OFB mode (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -M ofb",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'ofb_mode_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_negative_output_matches' => [qr/Decryption\sfailed/i],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'mode mismatch (tcp/22 ssh)',
'err_msg' => 'server accepted mismatch enc mode',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -M ecb",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Decryption\sfailed/i],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
### --pcap-file
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => '--pcap-file processing',
'err_msg' => 'could not complete SPA cycle',
'function' => \&process_pcap_file_directly,
'cmdline' => '',
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file " .
"--pcap-file $replay_pcap_file --foreground --verbose --verbose " .
"--verbose",
'server_positive_output_matches' => [qr/Replay\sdetected/i,
qr/candidate\sSPA/, qr/0x0000\:\s+2b/],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'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 (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -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' => 'multi port (tcp/60001,udp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001,udp/60001 -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' => 'multi port (tcp/22,udp/53,tcp/1234)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22,udp/53,tcp/1234 -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' => 'random SPA port (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_args -r",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str " .
qq|-P "udp"|,
'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",
'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'detect replay #1 (Rijndael prefix)',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'pkt_prefix' => 'U2FsdGVkX1',
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
'fatal' => $NO
},
### ensure iptables rules are not duplicated for identical access requests
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'iptables rules not duplicated',
'err_msg' => 'iptables rules duplicated',
'function' => \&iptables_rules_not_duplicated,
'cmdline' => "$default_client_args --test",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_negative_output_matches' => [qr/^2\s+ACCEPT\s.*$fake_ip/],
'fatal' => $NO
},
### backwards compatibility tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'client->server backwards compatibility',
'detail' => 'v2.0',
'err_msg' => 'backwards compatibility failed',
'function' => \&backwards_compatibility,
'no_ip_check' => 1,
'pkt' =>
'9ptGrLs8kVGVludcXFy17opvThEYzTeaT7RVlCN66W/G9QZs9BBevEQ0xxI8eCn' .
'KPDM+Bu9g0XwmCEVxxg+4jwBwtbCxVt9t5aSR29EVWZ6UAOwLkunK3t4FYBy1tL' .
'55krFt+1B2TtNSAH005kyDEZEOIGoY9Q/iU',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client->server backwards compatibility',
'detail' => 'v2.0.1',
'err_msg' => 'backwards compatibility failed',
'function' => \&backwards_compatibility,
'no_ip_check' => 1,
'pkt' =>
'+uAD6hlS2BHuaCtVKIGyIsB/4U8USqcP9o4aT6FvBuPKORwTV8byyzv6bzZYINs4' .
'Voq3QvBbIwkXJ63/oU+XxvP5R+DBLEnh3e/NHPFK6NB0WT2dujVyVxwBfvvWjIqW' .
'Hhro2tH34nqfTRIpevfLTMx7r+N8ZQ4V8',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client->server backwards compatibility',
'detail' => 'v2.0.2',
'err_msg' => 'backwards compatibility failed',
'function' => \&backwards_compatibility,
'no_ip_check' => 1,
'pkt' =>
'+mS70t2A2YmV50KgwDyy6nYLwzQ7AUO8pA/eatm7g9xc83xy1z7VOXeAYrgAOWy' .
'Ksk30QvkwHtPhl7I0oDz1bO+2K2JbDbyc0KBBzVNMLgJcuYgEpOXPkX2XhcTsgQ' .
'Vw2/Va/aUjvEvNPtwuipQS6DLTzOw/qy+/g',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client->server backwards compatibility',
'detail' => 'v2.0.3',
'err_msg' => 'backwards compatibility failed',
'function' => \&backwards_compatibility,
'pkt' =>
'+8OtxmTJPgQmrXZ7hAqTopLBC/thqHNuPHTfR234pFuQOCZUikPe0inHmjfnQFnP' .
'Sop/Iy6v+BCn9D+QD7eT7JI6BIoKp14K+8iNgKaNw1BdfgF1XDulpkNEdyG0fXz5' .
'M+GledHfz2d49aYThoQ2Cr8Iw1ycViawY',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client->server backwards compatibility',
'detail' => 'v2.0.4',
'err_msg' => 'backwards compatibility failed',
'function' => \&backwards_compatibility,
'pkt' =>
'8Xm8U5vQ03T88UTCWbwO3t/aL6euZ8IgVbNdDVz3Bn6HkTcBqxcME95U/G3bCH' .
'vQznpnGb05Md4ZgexHZGzZdSwsP8iVtcZdsgCBfeO4Eqs8OaSMjJVF8SQ+Jmhu' .
'XZMcWgMsIzhpprJ7JX41DrWd0OtBnE3rVwsN0',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'Android compatibility',
'detail' => 'v4.1.2',
'err_msg' => 'Android compatibility failed',
'function' => \&backwards_compatibility,
'no_ip_check' => 1,
'pkt' =>
'+59hIQhS1RlmqYLXNM/hPxtBAQTB5y3UKZq13O+r6qmg+APdQ+HQ' .
'OI7d4QCsp14s8KJpW8qBzZ/n0aZCFCFdZnvdZeJJVboQu4jo' .
'QFKZ8mmKwR/5DIO7k3qrXYGxYP0bnHYsih0HIE6CzSHlBGSf' .
'DJR92YhjYtL4Q',
'server_positive_output_matches' => [qr/with expire time/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'android_legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
### fuzzing tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# "tcp/`perl -e '{print "1"x"40"}'`" -a 127.0.0.2 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a buffer overflow in the fwknopd servers prior to 2.0.3 from
# authenticated clients.
#
'fuzzing_pkt' =>
'+JzxeTGlc6lwwzbJSrYChKx8bonWBIPajwGfEtGOaoglcMLbTY/GGXo/nxqiN1LykFS' .
'lDFXgrkyx2emJ7NGzYqQPUYZxLdZRocR9aRIptvXLLIPBcIpJASi/TUiJlw7CDFMcj0' .
'ptSBJJUZi0tozpKHETp3AgqfzyOy5FNs38aZsV5/sDl3Pt+kF7fTZJ+YLbmYY4yCUz2' .
'ZUYoCaJ7X78ULyJTi5eT7nug',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long proto value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# "tcp`perl -e '{print "A"x"28"}'`/1" -a 127.0.0.2 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a buffer overflow in the fwknopd servers prior to 2.0.3 from
# authenticated clients.
#
'fuzzing_pkt' =>
'/im5MiJQmOdzqrdWXv+AjEtAm/HsLrdaTFcSw3ZskqpGOdDIrSCz3VXbFfv7qDkc5Y4' .
'q/k1mRXl9SGzpug87U5dZSyCdAr30z7/2kUFEPTGOQBi/x+L1t1pvdkm4xg13t09ldm' .
'5OD8KiV6qzqLOvN4ULJjvvJJWBZ9qvo/f2Q9Wf67g2KHiwS6EeCINAuMoUw/mNRQMa4' .
'oGnOXu3/DeWHJAwtSeh7EAr4',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long IP value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a `perl -e '{print "1"x"136"}'`.0.0.1 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a condition in which pre-2.0.3 fwknopd servers fail to properly validate
# allow IP addresses from malicious authenticated clients.
#
'fuzzing_pkt' =>
'93f2rhsXLmBoPicWvYTqrbp+6lNqvWDc8dzmX2s3settwjBGRAXm33TB9agibEphrBu' .
'3d+7DEsivZLDS6Kz0JwdjX7t0J9c8es+DVNjlLnPtVNcxhs+2kUzimNrgysIXQRJ+GF' .
'GbhdxiXCqdy1vWxWpdoaZmY/CeGIkpoFJFPbJhCRLLX25UMvMF2wXj02MpI4d3t1/6W' .
'DM3taM3kZsiFv6HxFjAhIEuQ1oAg2OgRGXkDmT3jDNZMHUm0d4Ahm9LonG7RbOxq/B0' .
'qUvY8lkymbwvjelVok7Lvlc06cRhN4zm32D4V05g0vQS3PlX9C+mgph9DeAPVX+D8iZ' .
'8lGrxcPSfbCOW61k0MP+q1EhLZkc1qAm5g2+2cLNZcoBNEdh3yj8OTPZJyBVw',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'negative port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# tcp/-33 -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'/weoc+pEuQknZo8ImWTQBB+/PwSJ2/TcrmFoSkxpRXX4+jlUxoJakHrioxh8rhLmAD9' .
'8E4lMnq+EbM2XYdhs2alpZ5bovAFojMsYRWwr/BvRO4Um4Fmo9z9sY3DR477TXNYXBR' .
'iGXWxSL4u+AWSSePK3qiiYoRQVw',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'null port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/ \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'94nu7hvq6V/3A27GzjHwfPnPCQfs44ySlraIFYHOAqy5YqjkrBS67nH35tX55N1BrYZ' .
'07zvcT03keUhLE1Uo7Wme1nE7BfTOG5stmIK1UQI85sL52//lDHu+xCqNcL7GUKbVRz' .
'ekw+EUscVvUkrsRcVtSvOm+fCNo',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'long FKO protocol value (enc mode trigger)',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and is designed
# to have fwknopd look for a mode decryption mode for a long Rijndael-
# encrypted SPA packet
#
'fuzzing_pkt' =>
'/ewH/k1XsDX+VQ8NlNvCZ4P2QOl/4IpJYXkq4TtAe3899OtApXJiTtPCuYW70XPuxge' .
'MtFjc4UfslK/r9v+FYfyd3fIIHCz0Q0M4+nM3agTLmJj8nOxk6ZeBj82SDQWhHAxGdJ' .
'IQALPve0ug4cuGxS3b4M+2Q/Av9i2tU3Lzlogw3sY0tk6wGf4zZk4UsviVXYpINniGT' .
'RhYSIQ1dfdkng7hKiHMDaObYY1GFp4nxEt/QjasAwvE+7/iFyoKN+IRpGG4v4hGEPh2' .
'vTDqmvfRuIHtgFD7NxZjt+m/jjcu0gkdWEoD4fenwGU35FlvchyM2AiAEw7yRzSABfn' .
'R9d3sYZGMtyASw2O1vSluwIxUUnDop3gxEIhJEj8h+01pA3K+klSpALeY9EZgHqYC7E' .
'ETuPS6dZ3764nWohtCY67JvNUX7TtNDNc2qrhrapdRP17+PT2Vh4s9m38V3WwVWC3uH' .
'X/klLZcHIt+aRDV+uekw9GOKSgwFL2ekPpr3gXxigc3zrxel5hcsqLOpVUa4CP/0HkG' .
'F0NPQvOT3ZvpeIJnirKP1ZX9gDFinqhuzL7oqktW61e1iwe7KZEdrZV0k2KZwyb8qU5' .
'rPAEnw',
'server_positive_output_matches' => [qr/No\sstanza\sencryption\smode\smatch/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'long FKO protocol value (Rijndael trigger)',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and is designed
# to have fwknopd look for a mode decryption mode for a long Rijndael-
# encrypted SPA packet
#
'fuzzing_pkt' =>
'+YQNu4BFgiNeu8HeiBiNKriqCFSseALt9vJaKzkzK/OF4pjkJcvhGEOi7fEVXqn3VIdlGR' .
'DmBul2I7H3z18U9E97bWGgT9NexKgEPCuekL18ZEPf5xR3JleNsNWatqYgAOkgN8ZWE69Q' .
'qQUYYhxTvJHS6R+5JqFKB3A44hMXoICdYNkn9MAktHxk3PbbpQ+nA+jESwVCra2doAiLiM' .
'ucvGIZZiTv0Mc1blFYIE2zqZ/C7ct1V+ukwSkUv0r87eA7uJhmlpThRsL0dN6iekJ6i87B' .
'tE8QyuOXzOMftI11SUn/LwqD4RMdR21rvLrzR6ZB5eUX2UBpODyzX6n+PJJkTWCuFVT4z1' .
'MKY',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'null proto value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A /22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'/JT14qxh9P4iy+CuUZahThaQjoEuL2zd46a+jL6sTrBZJSa6faUX4dH5fte/4ZJv+9f' .
'd/diWYKAUvdQ4DydPGlR7mwQa2W+obKpqrsTBz7D4054z6ATAOGpCtifakEVl1XRc2+' .
'hW04WpY8mdUNu9i+PrfPr7/KxqU',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'invalid NAT IP',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose \
# --verbose -N 999.1.1.1:22
'fuzzing_pkt' =>
'/v/kYVOqw+NCkg8CNEphPPvH3dOAECWjqiF+NNYnK7yKHer/Gy8wCVNa/Rr/Wnm' .
'siApB3jrXEfyEY3yebJV+PHoYIYC3+4Trt2jxw0m+6iR231Ywhw1JetIPwsv7iQ' .
'ATvSTpZ+qiaoN0PPfy0+7yM6KlaQIu7bfG5E2a6VJTqTZ1qYz3H7QaJfbAtOD8j' .
'yEkDgP5+f49xrRA',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging_nat'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'FUZZING',
'subcategory' => 'server',
'detail' => 'invalid SOURCE access.conf',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_source'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'positive_output_matches' => [qr/Fatal\sinvalid/],
'exec_err' => $YES,
'fatal' => $NO
},
{
'category' => 'FUZZING',
'subcategory' => 'server',
'detail' => 'invalid OPEN_PORTS access.conf',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_open_ports'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'positive_output_matches' => [qr/Fatal\sinvalid/],
'exec_err' => $YES,
'fatal' => $NO
},
{
'category' => 'FUZZING',
'subcategory' => 'server',
'detail' => 'invalid RESTRICT_PORTS access.conf',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&generic_exec,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_restrict_ports'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'positive_output_matches' => [qr/Fatal\sinvalid/],
'exec_err' => $YES,
'fatal' => $NO
},
### command execution tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'command execution',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cmd_exec_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
qq|$fwknopCmd --server-cmd "echo fwknoptest > $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --get-key $local_key_file " .
"--verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'cmd_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'detect replay #2 (Rijndael prefix)',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'pkt_prefix' => 'U2FsdGVkX1',
'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' => 'server',
'detail' => 'ipfw active/expire sets not equal',
'err_msg' => 'allowed active/expire sets to be the same',
'function' => \&spa_cycle,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'ipfw_active_expire'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'server_positive_output_matches' => [qr/Cannot\sset\sidentical\sipfw\sactive\sand\sexpire\ssets/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'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
},
### perl module checks
{
'category' => 'perl FKO module',
'subcategory' => 'compile/install',
'detail' => 'to: ./FKO',
'err_msg' => 'could not install FKO module',
'function' => \&perl_fko_module_compile_install,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'FUZZING',
'detail' => 'generate invalid SPA pkts',
'err_msg' => 'could not generate invalid SPA pkts',
'function' => \&perl_fko_module_assume_patches_generate_fuzzing_spa_packets,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'FUZZING',
'detail' => 'generate invalid encoded pkts',
'err_msg' => 'could not generate invalid SPA pkts',
'function' => \&perl_fko_module_assume_patches_generate_fuzzing_encoding_spa_packets,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'create/destroy FKO object',
'err_msg' => 'could not create/destroy FKO object',
'function' => \&perl_fko_module_new_object,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'create/destroy 1000 FKO objects',
'err_msg' => 'could not create/destroy FKO object',
'function' => \&perl_fko_module_new_objects_1000,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko version',
'err_msg' => 'could not get libfko version',
'function' => \&perl_fko_module_version,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get random data',
'err_msg' => 'could not get libfko random data',
'function' => \&perl_fko_module_rand,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set username',
'err_msg' => 'could not get libfko username',
'function' => \&perl_fko_module_user,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko timestamp',
'err_msg' => 'could not get libfko timestamp',
'function' => \&perl_fko_module_timestamp,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set msg types',
'err_msg' => 'could not get/set libfko msg types',
'function' => \&perl_fko_module_msg_types,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set access msgs',
'err_msg' => 'could not get/set libfko access msgs',
'function' => \&perl_fko_module_access_msgs,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set NAT access msgs',
'err_msg' => 'could not get/set libfko NAT access msgs',
'function' => \&perl_fko_module_nat_access_msgs,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set cmd msgs',
'err_msg' => 'could not get/set libfko cmd msgs',
'function' => \&perl_fko_module_cmd_msgs,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'basic ops',
'detail' => 'libfko get/set client timeout',
'err_msg' => 'could not get/set libfko client timeout',
'function' => \&perl_fko_module_client_timeout,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'encrypt/decrypt',
'detail' => 'libfko complete cycle',
'err_msg' => 'could not finish complete cycle',
'function' => \&perl_fko_module_complete_cycle,
'set_legacy_iv' => $NO,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'encrypt/decrypt',
'detail' => 'libfko complete cycle (lIV)',
'err_msg' => 'could not finish complete cycle',
'function' => \&perl_fko_module_complete_cycle,
'set_legacy_iv' => $YES,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'encrypt/decrypt',
'detail' => 'truncated keys',
'err_msg' => 'allowed truncated keys to decrypt SPA data',
'function' => \&perl_fko_module_rijndael_truncated_keys,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'encrypt/decrypt',
'detail' => 'complete cycle (mod reuse)',
'err_msg' => 'could not finish complete cycle',
'function' => \&perl_fko_module_complete_cycle_module_reuse,
'set_legacy_iv' => $NO,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'encrypt/decrypt',
'detail' => 'complete cycle (mod reuse, lIV)',
'err_msg' => 'could not finish complete cycle',
'function' => \&perl_fko_module_complete_cycle_module_reuse,
'set_legacy_iv' => $YES,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'fuzzing data',
'detail' => 'legacy IV REPLPKTS',
'err_msg' => 'server accepted fuzzing pkts',
'function' => \&perl_fko_module_full_fuzzing_packets,
'set_legacy_iv' => $YES,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'fuzzing data',
'detail' => 'non-legacy IV REPLPKTS',
'err_msg' => 'server accepted fuzzing pkts',
'function' => \&perl_fko_module_full_fuzzing_packets,
'set_legacy_iv' => $NO,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'compatibility',
'detail' => 'client FKO -> C server',
'err_msg' => 'invalid SPA packet data',
'function' => \&perl_fko_module_client_compatibility,
'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' => 'perl FKO module',
'subcategory' => 'compatibility',
'detail' => 'FKO -> C invalid legacy IV',
'err_msg' => 'invalid SPA packet data',
'function' => \&perl_fko_module_client_compatibility,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file " .
"$intf_str",
'server_positive_output_matches' => [qr/Decryption failed/],
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'fatal' => $NO
},
{
'category' => 'perl FKO module',
'subcategory' => 'compatibility',
'detail' => 'FKO -> C valid legacy IV',
'err_msg' => 'invalid SPA packet data',
'function' => \&perl_fko_module_client_compatibility,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'def'} -a $cf{'legacy_iv_access'} " .
"-d $default_digest_file -p $default_pid_file " .
"$intf_str",
'set_legacy_iv' => $YES,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
### no password GPG testing
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
"$valgrind_str $fwknopdCmd -c $cf{'def'} " .
"-a $cf{'multi_gpg_no_pw_access'} $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' => 'GPG (no pw) 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_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -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_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) SPA',
'subcategory' => 'client+server',
'detail' => 'replay attack detection',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'cmdline' => "$default_client_gpg_args_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
'fatal' => $NO
},
{
'category' => 'GPG (no pw) SPA',
'subcategory' => 'client+server',
'detail' => 'detect replay #1 (GnuPG prefix)',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'pkt_prefix' => 'hQ',
'cmdline' => "$default_client_gpg_args_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) 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_no_homedir "
. "--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fatal' => $NO
},
### GPG testing (with passwords associated with keys) - first check to
### see if pinentry is required and disable remaining GPG tests if so
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'pinentry not required',
'err_msg' => 'could not complete SPA cycle',
'function' => \&gpg_pinentry_check,
'cmdline' => $default_client_gpg_args,
'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' => 'rc file default key (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_gpg_args_no_get_key " .
"--rc-file $cf{'rc_file_def_key'}",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'key_file' => $cf{'rc_file_def_key'},
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'rc file named key (tcp/22 ssh)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "$default_client_gpg_args_no_get_key " .
"--rc-file $cf{'rc_file_named_key'} -n testssh",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'key_file' => $cf{'rc_file_named_key'},
'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 $cf{'def'} " .
"-a $cf{'multi_gpg_access'} $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 (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -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,
'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'detect replay #2 (GnuPG prefix)',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'pkt_prefix' => 'hQ',
'cmdline' => $default_client_gpg_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'detect replay #3 (GnuPG prefix)',
'err_msg' => 'could not detect replay attack',
'function' => \&replay_detection,
'pkt_prefix' => 'hQ',
'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' => '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
},
{
'category' => 'profile coverage',
'detail' => 'gcov profile coverage',
'err_msg' => 'profile coverage failed',
'function' => \&profile_coverage,
'fatal' => $NO
},
);
my %test_keys = (
'category' => $REQUIRED,
'subcategory' => $OPTIONAL,
'detail' => $REQUIRED,
'function' => $REQUIRED,
'binary' => $OPTIONAL,
'cmdline' => $OPTIONAL,
'fwknopd_cmdline' => $OPTIONAL,
'fatal' => $OPTIONAL,
'key_file' => $OPTIONAL,
'exec_err' => $OPTIONAL,
'fw_rule_created' => $OPTIONAL,
'fw_rule_removed' => $OPTIONAL,
'server_conf' => $OPTIONAL,
'pkt_prefix' => $OPTIONAL,
'no_ip_check' => $OPTIONAL,
'set_legacy_iv' => $OPTIONAL,
'positive_output_matches' => $OPTIONAL,
'negative_output_matches' => $OPTIONAL,
'server_positive_output_matches' => $OPTIONAL,
'server_negative_output_matches' => $OPTIONAL,
'replay_positive_output_matches' => $OPTIONAL,
'replay_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);
if ($test_limit > 0) {
last if $executed >= $test_limit;
}
}
if ($use_valgrind) {
&run_test(
{
'category' => 'valgrind output',
'subcategory' => 'flagged functions',
'detail' => '',
'err_msg' => 'could not parse flagged functions',
'function' => \&parse_valgrind_flagged_functions,
'fatal' => $NO
}
);
}
&logr("\n");
if ($enable_openssl_compatibility_tests) {
&logr("[+] $openssl_success_ctr/$openssl_failure_ctr/$openssl_ctr " .
"OpenSSL tests passed/failed/executed\n");
}
if ($fuzzing_ctr > 0) {
&logr("[+] $fuzzing_success_ctr/$fuzzing_failure_ctr/$fuzzing_ctr " .
"Fuzzing tests passed/failed/executed\n");
}
&logr("[+] $passed/$failed/$executed tests passed/failed/executed\n\n");
copy $logfile, "$output_dir/$logfile" or die $!;
if ($pinentry_fail) {
if ($killall_path) {
### kill all gpg processes in the fwknop client
### process group (this will kill the test suite
### too, but we're already done)
system "$killall_path -g fwknop";
}
}
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'}";
$msg =~ s/REPLPKTS/-->$total_fuzzing_pkts<-- pkts/;
return unless &process_include_exclude($msg);
if ($list_mode) {
print $msg, "\n";
return;
}
&dots_print($msg);
$executed++;
$curr_test_file = "$output_dir/$executed.test";
$server_test_file = "$output_dir/${executed}_fwknopd.test";
&write_test_file("[+] TEST: $msg\n", $curr_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.";
}
}
if ($enable_perl_module_fuzzing_spa_pkt_generation) {
if ($msg =~ /perl FKO module.*FUZZING/) {
print "\n[+] Wrote $fuzzing_num_pkts fuzzing SPA ",
"packets to $fuzzing_pkts_file.tmp...\n\n";
exit 0;
}
}
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 or ($use_valgrind
and $msg =~ /valgrind\soutput/)) {
$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() {
$diff_dir1 = "${output_dir}.last" unless $diff_dir1;
$diff_dir2 = $output_dir unless $diff_dir2;
die "[*] Need results from a previous run before running --diff"
unless -d $diff_dir2;
die "[*] Current results set does not exist." unless -d $diff_dir1;
my %curr_tests = ();
my %prev_tests = ();
### Only diff results for matching tests (parse the logfile to see which
### test numbers match across the two test cycles).
&build_results_hash(\%curr_tests, $diff_dir1);
&build_results_hash(\%prev_tests, $diff_dir2);
for my $test_msg (sort {$curr_tests{$a}{'num'} <=> $curr_tests{$b}{'num'}}
keys %curr_tests) {
my $curr_result = $curr_tests{$test_msg}{'pass_fail'};
my $curr_num = $curr_tests{$test_msg}{'num'};
if (defined $prev_tests{$test_msg}) {
print "[+] Diff check: $test_msg\n";
my $prev_result = $prev_tests{$test_msg}{'pass_fail'};
my $prev_num = $prev_tests{$test_msg}{'num'};
if ($curr_result ne $prev_result) {
print " ** Verdict diff: current: $curr_result, ",
"previous: $prev_result $test_msg\n";
}
&diff_results($prev_num, $curr_num, $diff_dir1, $diff_dir2);
print "\n";
}
}
if (-d "$diff_dir1/$valgrind_cov_dir"
and -d "$diff_dir2/$valgrind_cov_dir") {
&diff_valgrind_results(\%curr_tests, \%prev_tests)
}
exit 0;
}
sub diff_valgrind_results() {
my ($curr_tests_hr, $prev_tests_hr) = @_;
print "\n\n\n[+] Valgrind differences:\n\n";
for my $test_msg (sort {$curr_tests_hr->{$a}->{'num'} <=> $curr_tests_hr->{$b}->{'num'}}
keys %$curr_tests_hr) {
my $curr_num = $curr_tests_hr->{$test_msg}->{'num'};
if (defined $prev_tests_hr->{$test_msg}) {
print "[+] Valgrind diff check: $test_msg\n";
my $prev_result = $prev_tests_hr->{$test_msg}->{'pass_fail'};
my $prev_num = $prev_tests_hr->{$test_msg}->{'num'};
&diff_results($prev_num, $curr_num,
"$diff_dir1/$valgrind_cov_dir", "$diff_dir2/$valgrind_cov_dir");
print "\n";
}
}
return;
}
sub diff_results() {
my ($prev_num, $curr_num, $dir1, $dir2) = @_;
### 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 ("$dir1/${prev_num}.test",
"$dir1/${prev_num}_fwknopd.test",
"$dir2/${curr_num}.test",
"$dir2/${curr_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 "$dir1/${prev_num}.test"
and -e "$dir2/${curr_num}.test") {
system "diff -u $dir1/${prev_num}.test " .
"$dir2/${curr_num}.test";
}
if (-e "$dir1/${prev_num}_fwknopd.test"
and -e "$dir2/${curr_num}_fwknopd.test") {
system "diff -u $dir1/${prev_num}_fwknopd.test " .
"$dir2/${curr_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() {
my $curr_pwd = cwd() or die $!;
chdir '..' or die $!;
### 'make clean' as root
unless (&run_cmd('make clean', $cmd_out_tmp,
"test/$curr_test_file")) {
chdir $curr_pwd or die $!;
return 0;
}
if ($sudo_path) {
my $username = getpwuid((stat($configure_path))[4]);
die "[*] Could not determine $configure_path owner"
unless $username;
unless (&run_cmd("$sudo_path -u $username make",
$cmd_out_tmp, "test/$curr_test_file")) {
unless (&run_cmd('make', $cmd_out_tmp,
"test/$curr_test_file")) {
chdir $curr_pwd or die $!;
return 0;
}
}
} else {
unless (&run_cmd('make', $cmd_out_tmp,
"test/$curr_test_file")) {
chdir $curr_pwd or die $!;
return 0;
}
}
### look for compilation warnings - something like:
### warning: test is used uninitialized in this function
if (&file_find_regex([qr/\swarning:\s/i, qr/gcc\:.*\sunused/],
$MATCH_ANY, "test/$curr_test_file")) {
chdir $curr_pwd or die $!;
return 0;
}
chdir $curr_pwd or die $!;
### the new binaries should exist
unless (-e $fwknopCmd and -x $fwknopCmd) {
&write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
$curr_test_file);
}
unless (-e $fwknopdCmd and -x $fwknopdCmd) {
&write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
$curr_test_file);
}
return 1;
}
sub profile_coverage() {
### check for any *.gcno files - if they don't exist, then fwknop was
### not compiled with profile support
unless (glob('../client/*.gcno') and glob('../server/*.gcno')) {
&write_test_file("[-] ../client/*.gcno and " .
"../server/*.gcno files do not exist.\n", $curr_test_file);
return 0;
}
my $curr_dir = getcwd() or die $!;
### gcov -b ../client/*.gcno
for my $dir ('../client', '../server', '../lib/.libs') {
next unless -d $dir;
chdir $dir or die $!;
system "$gcov_path -b -u *.gcno > /dev/null 2>&1";
chdir $curr_dir or die $!;
&run_cmd(qq|grep "called 0 returned" $dir/*.gcov|,
$cmd_out_tmp, $curr_test_file);
}
return 1;
}
sub make_distcheck() {
### 'make clean' as root
return 0 unless &run_cmd('make -C .. distcheck',
$cmd_out_tmp, $curr_test_file);
### look for compilation warnings - something like:
### warning: test is used uninitialized in this function
return 1 if &file_find_regex([qr/archives\sready\sfor\sdistribution/],
$MATCH_ALL, $curr_test_file);
return 0;
}
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, and libfko.dylib path on Mac OS X)
if ($test_hr->{'binary'} =~ /libfko/) {
unless (-e $test_hr->{'binary'}) {
my $file = "$lib_dir/libfko.dylib";
if (-e $file) {
$test_hr->{'binary'} = $file;
$libfko_bin = $file;
} else {
for my $f (glob("$lib_dir/libfko.so*")) {
if (-e $f and -x $f) {
$test_hr->{'binary'} = $f;
$libfko_bin = $f;
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",
$curr_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, $curr_test_file);
return 1 if &file_find_regex([qr/$version/],
$MATCH_ALL, $curr_test_file);
}
return 0;
}
sub client_send_spa_packet() {
my $test_hr = shift;
my $rv = 1;
&write_key($default_key, $local_key_file);
$rv = 0 unless &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $curr_test_file);
$rv = 0 unless &file_find_regex([qr/final\spacked/i],
$MATCH_ALL, $curr_test_file);
if ($enable_openssl_compatibility_tests
and $test_hr->{'detail'} !~ /iptables.*not\sduplicated/) {
### extract the SPA packet from the cmd tmp file before
### openssl command execution overwrites it
my $encoded_msg = '';
my $digest = '';
my $enc_mode = 0;
my $is_hmac_mode = 1;
open F, "< $cmd_out_tmp" or die $!;
while (<F>) {
if (/^\s+Encoded\sData\:\s+(\S+)/) {
$encoded_msg = $1;
} elsif (/Data\sDigest\:\s(\S+)/) {
$digest = $1;
} elsif (/Encryption\sMode\:\s+(\d+)/) {
$enc_mode = $1;
} elsif (/^\s+HMAC.*\:\s\<NULL\>/) {
$is_hmac_mode = 0;
}
}
close F;
$encoded_msg .= ":$digest";
my $ssl_test_flag = $REQUIRE_SUCCESS;
$ssl_test_flag = $REQUIRE_FAILURE if $enc_mode != 2; ### CBC mode
$ssl_test_flag = $REQUIRE_FAILURE if $is_hmac_mode;
my $encrypted_msg = &get_spa_packet_from_file($cmd_out_tmp);
my $key = '';
if ($test_hr->{'key_file'}) {
open F, "< $test_hr->{'key_file'}" or die $!;
while (<F>) {
if (/^KEY_BASE64\s+(\S+)/) {
$key = decode_base64($1);
}
}
close F;
}
$key = $default_key unless $key;
unless (&openssl_verification($encrypted_msg,
$encoded_msg, '', $key, $ssl_test_flag)) {
$rv = 0;
}
}
return $rv;
}
sub permissions_check() {
my $test_hr = shift;
my $rv = 0;
chmod 0777, $cf{'def'} or die $!;
chmod 0777, $cf{'def_access'} or die $!;
$rv = &spa_cycle($test_hr);
chmod 0600, $cf{'def'} or die $!;
chmod 0600, $cf{'def_access'} or die $!;
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
return $rv;
}
sub rotate_digest_file() {
my $test_hr = shift;
my $rv = 1;
$rv = &spa_cycle($test_hr);
if (-e "${default_digest_file}-old") {
### put the file back in place
move "${default_digest_file}-old", $default_digest_file;
&write_test_file("[+] digest cache file was rotated.\n",
$curr_test_file);
} else {
&write_test_file("[-] rotated digest cache file does not exist.\n",
$curr_test_file);
$rv = 0;
}
return $rv;
}
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->{'client_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'client_positive_output_matches'},
$MATCH_ALL, $curr_test_file);
}
if ($test_hr->{'client_negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'client_negative_output_matches'},
$MATCH_ANY, $curr_test_file);
}
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
if ($test_hr->{'server_negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'server_negative_output_matches'},
$MATCH_ANY, $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/],
$MATCH_ALL, $curr_test_file)) {
$rv = 0;
}
unless (&file_find_regex([qr/Username:\s*$spoof_user/],
$MATCH_ALL, $server_test_file)) {
$rv = 0;
}
return $rv;
}
sub gpg_pinentry_check() {
my $test_hr = shift;
my $rv = 1;
my $pid;
if ($pid = fork()) {
local $SIG{'ALRM'} = sub {die "[*] External script timeout.\n"};
alarm 5; ### running the client should be fast
eval {
waitpid($pid, 0);
};
alarm 0;
if ($@) {
$rv = 0;
push @tests_to_exclude, qr/GPG/;
$pinentry_fail = 1;
}
} else {
die "[*] Could not run the fwknop client: $!" unless defined $pid;
exec qq{$test_hr->{'cmdline'} > /dev/null 2>&1 };
}
return $rv;
}
sub perl_fko_module_compile_install() {
my $test_hr = shift;
my $rv = 1;
if (-d $perl_mod_fko_dir) {
rmtree $perl_mod_fko_dir or die $!;
}
mkdir $perl_mod_fko_dir or die "[*] Could not mkdir $perl_mod_fko_dir: $!";
my $curr_pwd = cwd() or die $!;
chdir '../perl/FKO' or die $!;
&run_cmd("make clean", $cmd_out_tmp, "../../test/$curr_test_file")
if -e 'Makefile' or -e 'Makefile.old';
&run_cmd("perl Makefile.PL PREFIX=../../test/$perl_mod_fko_dir " .
"LIB=../../test/$perl_mod_fko_dir", $cmd_out_tmp,
"../../test/$curr_test_file");
&run_cmd('make', $cmd_out_tmp, "../../test/$curr_test_file");
if (&file_find_regex([qr/rerun\sthe\smake\scommand/],
$MATCH_ALL, "../../test/$curr_test_file")) {
&run_cmd('touch Makefile.PL', $cmd_out_tmp, "../../test/$curr_test_file");
&run_cmd('touch Makefile', $cmd_out_tmp, "../../test/$curr_test_file");
&run_cmd('make', $cmd_out_tmp, "../../test/$curr_test_file");
}
&run_cmd('make install', $cmd_out_tmp, "../../test/$curr_test_file");
chdir $curr_pwd or die $!;
my $mod_paths_ar = &get_mod_paths();
if ($#$mod_paths_ar > -1) { ### FKO/ exists
push @$mod_paths_ar, @INC;
splice @INC, 0, $#$mod_paths_ar+1, @$mod_paths_ar;
}
eval { require FKO };
if ($@) {
&write_test_file("[-] could not 'require FKO' module: $@\n",
$curr_test_file);
$rv = 0;
### disable remaining perl module checks
push @tests_to_exclude, qr/perl FKO module/;
}
return $rv;
}
sub perl_fko_module_new_object() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
if ($fko_obj) {
$fko_obj->destroy();
} else {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
### disable remaining perl module checks
push @tests_to_exclude, qr/perl FKO module/;
$rv = 0;
}
return $rv;
}
sub perl_fko_module_new_objects_1000() {
my $test_hr = shift;
my $rv = 1;
for (my $i=0; $i < 1000; $i++) {
$fko_obj = FKO->new();
if ($fko_obj) {
$fko_obj->destroy();
} else {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
### disable remaining perl module checks
push @tests_to_exclude, qr/perl FKO module/;
$rv = 0;
last;
}
}
return $rv;
}
sub perl_fko_module_version() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $version = $fko_obj->version();
if ($version) {
&write_test_file("[+] got version(): $version\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get version()\n",
$curr_test_file);
$rv = 0;
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_rand() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $rand_value = $fko_obj->rand_value();
if ($rand_value) {
&write_test_file("[+] got rand_value(): $rand_value\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get rand_value()\n",
$curr_test_file);
$rv = 0;
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_user() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $username = $fko_obj->username();
if ($username) {
&write_test_file("[+] got username(): $username\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get username()\n",
$curr_test_file);
$rv = 0;
}
my $status = 0;
for my $user (@{&valid_usernames()}) {
### set the username and check it
$status = $fko_obj->username($user);
if ($status == FKO->FKO_SUCCESS and $fko_obj->username() eq $user) {
&write_test_file("[+] get/set username(): $user\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get/set username(): $user " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
}
}
for my $fuzzing_user (@{&fuzzing_usernames()}) {
### set the username to something fuzzing and make sure libfko rejects it
$status = $fko_obj->username($fuzzing_user);
if ($status == FKO->FKO_SUCCESS and $fko_obj->username() eq $fuzzing_user) {
&write_test_file("[-] libfko allowed fuzzing username(): $fuzzing_user " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko threw out fuzzing username(): $fuzzing_user\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_timestamp() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $curr_time = $fko_obj->timestamp();
if ($curr_time) {
&write_test_file("[+] got current timestamp(): $curr_time\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get timestamp()\n",
$curr_test_file);
$rv = 0;
}
for my $offset (@{&valid_time_offsets()}) {
$fko_obj->timestamp($offset);
my $spa_timestamp = $fko_obj->timestamp();
if (abs($spa_timestamp - $curr_time) < (abs($offset) + 10)) {
&write_test_file("[+] set valid timestamp() offset: $offset\n",
$curr_test_file);
} else {
&write_test_file("[-] timestamp() offset: $offset not accepted.\n",
$curr_test_file);
$rv = 0;
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_client_timeout() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $valid_timeout = 30;
my $status = $fko_obj->spa_client_timeout($valid_timeout);
if ($status == FKO->FKO_SUCCESS and $fko_obj->spa_client_timeout() == $valid_timeout) {
&write_test_file("[+] got spa_client_timeout(): $valid_timeout\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get spa_client_timeout()\n",
$curr_test_file);
$rv = 0;
}
for my $fuzzing_client_timeout (@{&fuzzing_client_timeouts()}) {
### set message timeout and then see if it matches
my $status = $fko_obj->spa_client_timeout($fuzzing_client_timeout);
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] libfko allowed fuzzing spa_client_timeout(): $fuzzing_client_timeout " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko rejected fuzzing spa_client_timeout(): $fuzzing_client_timeout\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_msg_types() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
my $msg_type = -1;
### default
$msg_type = $fko_obj->spa_message_type();
if ($msg_type > -1) {
&write_test_file("[+] got default spa_message_type(): $msg_type\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get default spa_message_type()\n",
$curr_test_file);
$rv = 0;
}
for my $type (@{&valid_spa_message_types()}) {
### set message type and then see if it matches
my $status = $fko_obj->spa_message_type($type);
if ($status == FKO->FKO_SUCCESS and $fko_obj->spa_message_type() == $type) {
&write_test_file("[+] get/set spa_message_type(): $type\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get/set spa_message_type(): $type " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
last;
}
}
for my $fuzzing_type (@{&fuzzing_spa_message_types()}) {
### set message type and then see if it matches
my $status = $fko_obj->spa_message_type($fuzzing_type);
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] libfko allowed fuzzing spa_message_type(): $fuzzing_type " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko rejected fuzzing spa_message_type(): $fuzzing_type\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_access_msgs() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
for my $msg (@{valid_access_messages()}) {
### set message and then see if it matches
my $status = $fko_obj->spa_message($msg);
if ($status == FKO->FKO_SUCCESS and $fko_obj->spa_message() eq $msg) {
&write_test_file("[+] get/set spa_message(): $msg\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get/set spa_message(): $msg " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
last;
}
}
for my $fuzzing_msg (@{&fuzzing_access_messages()}) {
### set message type and then see if it matches
my $status = $fko_obj->spa_message($fuzzing_msg);
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] libfko allowed fuzzing " .
"spa_message(): $fuzzing_msg, got: " . $fko_obj->spa_message() . ' ' .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko rejected fuzzing spa_message(): $fuzzing_msg\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_nat_access_msgs() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message_type(FKO->FKO_NAT_ACCESS_MSG);
for my $msg (@{valid_nat_access_messages()}) {
### set message and then see if it matches
my $status = $fko_obj->spa_nat_access($msg);
if ($status == FKO->FKO_SUCCESS and $fko_obj->spa_nat_access() eq $msg) {
&write_test_file("[+] get/set spa_nat_access(): $msg\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get/set spa_nat_access(): $msg " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
last;
}
}
for my $fuzzing_msg (@{&fuzzing_nat_access_messages()}, @{&valid_access_messages()}) {
### set message type and then see if it matches
my $status = $fko_obj->spa_nat_access($fuzzing_msg);
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] libfko allowed fuzzing " .
"spa_nat_access(): $fuzzing_msg, got: " . $fko_obj->spa_nat_access() . ' ' .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko rejected fuzzing spa_nat_access(): $fuzzing_msg\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub perl_fko_module_cmd_msgs() {
my $test_hr = shift;
my $rv = 1;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message_type(FKO->FKO_COMMAND_MSG);
for my $msg (@{valid_cmd_messages()}) {
### set message and then see if it matches
my $status = $fko_obj->spa_message($msg);
if ($status == FKO->FKO_SUCCESS and $fko_obj->spa_message() eq $msg) {
&write_test_file("[+] get/set spa_message(): $msg\n",
$curr_test_file);
} else {
&write_test_file("[-] could not get/set spa_message(): $msg " .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
last;
}
}
for my $fuzzing_msg (@{&fuzzing_cmd_messages()}) {
### set message type and then see if it matches
my $status = $fko_obj->spa_message($fuzzing_msg);
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] libfko allowed fuzzing " .
"spa_message(): $fuzzing_msg, got: " . $fko_obj->spa_message() . ' ' .
FKO::error_str() . "\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] libfko rejected fuzzing spa_message(): $fuzzing_msg\n",
$curr_test_file);
}
}
$fko_obj->destroy();
return $rv;
}
sub valid_time_offsets() {
my @offsets = (
9999999,
10,
-10,
-999999,
);
return \@offsets;
}
sub fuzzing_client_timeouts() {
my @timeouts = (
-1,
-10,
-10000,
);
return \@timeouts;
}
sub valid_usernames() {
my @users = (
'test',
'root',
'mbr',
'test-test',
'test_test',
'someuser',
'someUser',
'USER',
'USER001',
'00001'
);
return \@users;
}
sub fuzzing_usernames() {
my @users = (
'A'x1000,
"-1",
-1,
# pack('a', ""),
'123%123',
'123$123',
'-user',
'_user',
'-User',
',User',
'part1 part2',
'a:b'
);
return \@users;
}
sub valid_encryption_keys() {
my @keys = (
'testtest',
'12341234',
'1',
'1234',
'a',
'$',
'!@#$%',
'asdfasdfsafsdaf',
);
return \@keys;
}
sub valid_spa_digest_types() {
my @types = (
FKO->FKO_DIGEST_MD5,
FKO->FKO_DIGEST_SHA1,
FKO->FKO_DIGEST_SHA256,
FKO->FKO_DIGEST_SHA384,
FKO->FKO_DIGEST_SHA512
);
return \@types;
}
sub fuzzing_spa_digest_types() {
my @types = (
-1,
-2,
255,
);
return \@types;
}
sub valid_spa_message_types() {
my @types = (
FKO->FKO_ACCESS_MSG,
FKO->FKO_COMMAND_MSG,
FKO->FKO_LOCAL_NAT_ACCESS_MSG,
FKO->FKO_NAT_ACCESS_MSG,
FKO->FKO_CLIENT_TIMEOUT_ACCESS_MSG,
FKO->FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG,
FKO->FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG,
);
return \@types;
}
sub fuzzing_spa_message_types() {
my @types = (
-1,
-2,
255,
);
return \@types;
}
sub valid_access_messages() {
my @msgs = (
'1.2.3.4,tcp/22',
'123.123.123.123,tcp/12345',
'1.2.3.4,udp/53',
'123.123.123.123,udp/12345',
'123.123.123.123,udp/12345,tcp/12345',
'1.1.1.1,udp/1,tcp/1,tcp/2,udp/3,tcp/4,tcp/12345',
# '123.123.123.123,icmp/1'
);
return \@msgs;
}
sub valid_nat_access_messages() {
my @msgs = (
'1.2.3.4,22',
'123.123.123.123,12345',
);
return \@msgs;
}
sub valid_cmd_messages() {
my @msgs = (
'1.2.3.4,cat /etc/hosts',
'123.123.123.123,cat /etc/hosts',
'123.123.123.123,echo blah > /some/file',
'1.1.1.1,echo blah > /some/file',
'1.1.1.1,' . 'A'x10,
'1.1.1.1,' . 'A'x10 . ':',
);
return \@msgs;
}
sub fuzzing_access_messages() {
my @msgs = ();
push @msgs, @{&fuzzing_nat_access_messages()};
push @msgs, '1.1.1.2,12345',
push @msgs, @{&valid_nat_access_messages()};
return \@msgs;
}
sub fuzzing_nat_access_messages() {
my @msgs = (
'1.2.3.4',
'1.2.3.4.',
'123.123.123.123',
'923.123.123.123',
'123.123.123.123.',
'999.999.999.999',
'1.2.3.4,tcp/2a2',
'1.2.3.4,tcp/22,',
'1.2.3.4,tcp/123456',
'1.2.3.4,tcp/123456' . '9'x100,
'1.2.3.4,tcp//22',
'1.2.3.4,tcp/22/',
'a23.123.123.123,tcp/12345',
'999.999.999.999,tcp/22',
'999.1.1.1,tcp/22',
-1,
1,
'A',
0x0,
'A'x1000,
'/'x1000,
'%'x1000,
':'x1000,
pack('a', ""),
'1.1.1.p/12345',
'1.1.1.2,,,,12345',
'1.1.1.2,icmp/123',
',,,',
'----',
'1.3.4.5.5',
'1.3.4.5,' . '/'x100,
'1.3.4.5,' . '/'x100 . '22',
'1.2.3.4,rcp/22',
'1.2.3.4,udp/-1',
'1.2.3.4,tcp/-1',
'1.2.3.4,icmp/-1',
'1.2.3' . pack('a', "") . '.4,tcp/22',
'1.2.3.' . pack('a', "") . '4,tcp/22',
'1.2.3.4' . pack('a', "") . ',tcp/22',
'1.2.3.4,' . pack('a', "") . 'tcp/22',
'1.2.3.4,t' . pack('a', "") . 'cp/22',
'1.2.3.4,tc' . pack('a', "") . 'p/22',
'1.2.3.4,tcp' . pack('a', "") . '/22',
'1.2.3.4,tcp/' . pack('a', "") . '22',
'123.123.123' . pack('a', "") . '.123,tcp/22',
'123.123.123.' . pack('a', "") . '123,tcp/22',
'123.123.123.1' . pack('a', "") . '23,tcp/22',
'123.123.123.12' . pack('a', "") . '3,tcp/22',
'123.123.123.123' . pack('a', "") . ',tcp/22',
'123.123.123.123,' . pack('a', "") . 'tcp/22',
'123.123.123.123,t' . pack('a', "") . 'cp/22',
'123.123.123.123,tc' . pack('a', "") . 'p/22',
'123.123.123.123,tcp' . pack('a', "") . '/22',
'123.123.123.123,tcp/' . pack('a', "") . '22',
'1.2.3.4,t' . pack('a', "") . 'cp/22',
'1.1.1.1,udp/1,tap/1,tcp/2,udp/3,tcp/4,tcp/12345',
'1.1.1.1,udp/1,tcp/-11,tcp/2,udp/3,tcp/4,tcp/12345',
'1.1.1.1,udp/1,tcp/1,tcp/2udp/3,tcp/4,tcp/12345',
'1.1.1.1,udp/1,tcp/1,tcp/2,udp/3,tcp/4,tcp////12345',
'1.1.1.1,udp/1,tcp/1,tcp/2udp/3,tcp/4,tcp////12345',
'1.1.1.1,udp/1,tcp/1,tcp/2udp/3,tcp/4,tcp////12345',
'1.1.1.1,udp/1,tcp/1,tcp/2udp/3*tcp/4,tcp////12345',
'1.1.1.1,udp/1,tcp/1,tcp/2udp/3,tcb/4,tcp////12345',
'1.1.1.1,udp/1,tcp/1tcp/2udp/3,tcp/4,tcp////12345',
'123.123.123.123udp/1,tcp/1,tcp/2udp/3,tcp/4,tcp////12345////////////',
);
return \@msgs;
}
sub fuzzing_cmd_messages() {
my @msgs = (
### must start with a valid IP, so test this
-1,
1,
'A',
0x0,
'A'x1000,
'/'x1000,
'%'x1000,
':'x1000,
pack('a', ""),
',,,',
'----',
'1.3.4.5.5',
'999.3.4.5',
'1.,',
'1.2.,',
'1.2.3.,',
'1.2.3.4',
'123.123.123.123',
'1.2.3.4,',
'1.2.3.4.',
'123.123.123.123,' . 'A'x1000,
);
return \@msgs;
}
sub perl_fko_module_rijndael_truncated_keys() {
my $test_hr = shift;
my $rv = 1;
for my $msg (@{valid_access_messages()}[0]) {
for my $user (@{valid_usernames()}[0]) {
for my $digest_type (@{valid_spa_digest_types()}[0]) {
my $key = '1';
for (my $i=20; $i <= 32; $i++) {
$key .= $i % 10;
&write_test_file("\n\n[+] ------ KEY: $key (" . length($key) . " bytes)\n",
$curr_test_file);
for (my $j=1; $j < length($key); $j++) {
&write_test_file("\n MSG: $msg, user: $user, " .
"digest type: $digest_type (orig key: $key)\n",
$curr_test_file);
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message($msg);
$fko_obj->username($user);
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type($digest_type);
$fko_obj->spa_data_final($key, length($key), '', 0);
my $encrypted_msg = $fko_obj->spa_data();
$fko_obj->destroy();
if ($enable_openssl_compatibility_tests) {
unless (&openssl_verification($encrypted_msg,
'', $msg, $key, $REQUIRE_SUCCESS)) {
$rv = 0;
}
}
### now get new object for decryption
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_data($encrypted_msg);
my $truncated_key = $key;
$truncated_key =~ s/^(.{$j}).*/$1/;
&write_test_file(" Trying truncated key: $truncated_key\n",
$curr_test_file);
if ($fko_obj->decrypt_spa_data($truncated_key,
length($truncated_key)) == FKO->FKO_SUCCESS) {
&write_test_file("[-] $msg decrypt success with truncated key " .
"($key -> $truncated_key)\n",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] $msg decrypt rejected truncated " .
"key ($key -> $truncated_key)\n",
$curr_test_file);
}
$fko_obj->destroy();
if ($enable_openssl_compatibility_tests) {
unless (&openssl_verification($encrypted_msg,
'', $msg, $truncated_key, $REQUIRE_FAILURE)) {
$rv = 0;
}
}
}
&write_test_file("\n", $curr_test_file);
}
}
}
}
return $rv;
}
sub perl_fko_module_complete_cycle() {
my $test_hr = shift;
my $rv = 1;
for my $msg (@{valid_access_messages()}) {
for my $user (@{valid_usernames()}) {
for my $digest_type (@{valid_spa_digest_types()}) {
for my $key (@{valid_encryption_keys()}) {
&write_test_file("[+] msg: $msg, user: $user, " .
"digest type: $digest_type, key: $key\n",
$curr_test_file);
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message($msg);
$fko_obj->username($user);
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type($digest_type);
$fko_obj->encryption_mode(FKO->FKO_ENC_MODE_CBC_LEGACY_IV)
if $test_hr->{'set_legacy_iv'} eq $YES;
$fko_obj->spa_data_final($key, length($key), '', 0);
my $encrypted_msg = $fko_obj->spa_data();
$fko_obj->destroy();
### now get new object for decryption
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_data($encrypted_msg);
$fko_obj->encryption_mode(FKO->FKO_ENC_MODE_CBC_LEGACY_IV)
if $test_hr->{'set_legacy_iv'} eq $YES;
$fko_obj->decrypt_spa_data($key, length($key));
if ($msg ne $fko_obj->spa_message()) {
&write_test_file("[-] $msg encrypt/decrypt mismatch\n",
$curr_test_file);
$rv = 0;
}
$fko_obj->destroy();
if ($enable_openssl_compatibility_tests) {
my $flag = $REQUIRE_SUCCESS;
$flag = $REQUIRE_FAILURE if $test_hr->{'set_legacy_iv'} eq $YES;
unless (&openssl_verification($encrypted_msg,
'', $msg, $key, $flag)) {
$rv = 0;
}
}
}
}
}
}
return $rv;
}
sub perl_fko_module_complete_cycle_module_reuse() {
my $test_hr = shift;
my $rv = 1;
for my $msg (@{valid_access_messages()}) {
for my $user (@{valid_usernames()}) {
for my $digest_type (@{valid_spa_digest_types()}) {
for my $key (@{valid_encryption_keys()}) {
&write_test_file("[+] msg: $msg, user: $user, " .
"digest type: $digest_type, key: $key\n",
$curr_test_file);
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message($msg);
$fko_obj->username($user);
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type($digest_type);
$fko_obj->encryption_mode(FKO->FKO_ENC_MODE_CBC_LEGACY_IV)
if $test_hr->{'set_legacy_iv'} eq $YES;
$fko_obj->spa_data_final($key, length($key), '', 0);
my $encrypted_msg = $fko_obj->spa_data();
$fko_obj->spa_data($encrypted_msg);
$fko_obj->decrypt_spa_data($key, length($key));
if ($msg ne $fko_obj->spa_message()) {
&write_test_file("[-] $msg encrypt/decrypt mismatch\n",
$curr_test_file);
$rv = 0;
}
$fko_obj->destroy();
if ($enable_openssl_compatibility_tests) {
my $flag = $REQUIRE_SUCCESS;
$flag = $REQUIRE_FAILURE if $test_hr->{'set_legacy_iv'} eq $YES;
unless (&openssl_verification($encrypted_msg,
'', $msg, $key, $flag)) {
$rv = 0;
}
}
}
}
}
}
return $rv;
}
sub perl_fko_module_assume_patches_generate_fuzzing_spa_packets() {
my $test_hr = shift;
my $rv = 1;
my @fuzzing_pkts = ();
USER: for my $user (@{&fuzzing_usernames()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->username($user);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Bogus user: $user triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next USER;
}
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Bogus user: '
. $fuzzing_test_tag
. "$user, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
MSG: for my $msg (@{&fuzzing_access_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
my $status = $fko_obj->spa_message($msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Bogus access_msg: $msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next MSG;
}
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Bogus access_msg: '
. $fuzzing_test_tag
. "$msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
NAT_MSG: for my $nat_msg (@{&fuzzing_nat_access_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->spa_nat_access($nat_msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Bogus NAT_access_msg: $nat_msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next NAT_MSG;
}
$fko_obj->spa_message_type(FKO->FKO_NAT_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Bogus NAT_access_msg: '
. $fuzzing_test_tag
. "$nat_msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
CMD: for my $msg (@{&fuzzing_cmd_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
my $status = $fko_obj->spa_message($msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Bogus cmd_msg: $msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next CMD;
}
$fko_obj->spa_message_type(FKO->FKO_COMMAND_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Bogus cmd_msg: '
. $fuzzing_test_tag
. "$msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
TYPE: for my $type (@{&fuzzing_spa_message_types()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->spa_message_type($type);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Bogus msg_type: $type triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next TYPE;
}
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Bogus msg_type: '
. $fuzzing_test_tag
. "$type, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
if ($fuzzing_pkts_append) {
open F, ">> $fuzzing_pkts_file.tmp" or die $!;
} else {
open F, "> $fuzzing_pkts_file.tmp" or die $!;
}
for my $pkt (@fuzzing_pkts) {
print F $pkt, "\n";
}
close F;
$fuzzing_num_pkts = $#fuzzing_pkts+1;
return $rv;
}
sub perl_fko_module_assume_patches_generate_fuzzing_encoding_spa_packets() {
my $test_hr = shift;
### this function assumes the lib/fko_encode.c has been patched to mess
### with final encoded SPA packet data just before encryption
my $rv = 1;
my @fuzzing_pkts = ();
USER: for my $user (@{&valid_usernames()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->username($user);
if ($status != FKO->FKO_SUCCESS) {
&write_test_file("[-] Invalid_encoding user: $user triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next USER;
}
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Invalid_encoding user: '
. $fuzzing_test_tag
. "$user, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
MSG: for my $msg (@{&valid_access_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
my $status = $fko_obj->spa_message($msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Invalid_encoding access_msg: $msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next MSG;
}
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Invalid_encoding access_msg: '
. $fuzzing_test_tag
. "$msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
NAT_MSG: for my $nat_msg (@{&valid_nat_access_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->spa_nat_access($nat_msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Invalid_encoding NAT_access_msg: $nat_msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next NAT_MSG;
}
$fko_obj->spa_message_type(FKO->FKO_NAT_ACCESS_MSG);
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Invalid_encoding NAT_access_msg: '
. $fuzzing_test_tag
. "$nat_msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
CMD: for my $msg (@{&valid_cmd_messages()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message_type(FKO->FKO_COMMAND_MSG);
my $status = $fko_obj->spa_message($msg);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Invalid_encoding cmd_msg: $msg triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next CMD;
}
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Invalid_encoding cmd_msg: '
. $fuzzing_test_tag
. "$msg, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
TYPE: for my $type (@{&valid_spa_message_types()}) {
$fko_obj = FKO->new();
unless ($fko_obj) {
die "[*] error FKO->new(): " . FKO::error_str();
}
$fko_obj->spa_message('1.2.3.4,tcp/22');
my $status = $fko_obj->spa_message_type($type);
if ($status != FKO->FKO_SUCCESS) {
### we expect that a patch has been applied to libfko to allow
### fuzzing data
&write_test_file("[-] Invalid_encoding msg_type: $type triggered a libfko error\n",
$curr_test_file);
$fko_obj->destroy();
$rv = 0;
next TYPE;
}
$fko_obj->digest_type(FKO->FKO_DIGEST_SHA256);
$fko_obj->spa_data_final($fuzzing_key, length($fuzzing_key), '', 0);
my $fuzzing_str = '[+] Invalid_encoding msg_type: '
. $fuzzing_test_tag
. "$type, SPA packet: "
. ($fko_obj->spa_data() || '(NULL)');
$fuzzing_str =~ s/[^\x20-\x7e]{1,}/(NA)/g;
push @fuzzing_pkts, $fuzzing_str;
&write_test_file("$fuzzing_str\n", $curr_test_file);
$fko_obj->destroy();
}
if ($fuzzing_pkts_append) {
open F, ">> $fuzzing_pkts_file.tmp" or die $!;
} else {
open F, "> $fuzzing_pkts_file.tmp" or die $!;
}
for my $pkt (@fuzzing_pkts) {
print F $pkt, "\n";
}
close F;
$fuzzing_num_pkts = $#fuzzing_pkts+1;
return $rv;
}
sub perl_fko_module_full_fuzzing_packets() {
my $test_hr = shift;
my $rv = 1;
for my $field (keys %fuzzing_spa_packets) {
for my $field_val (keys %{$fuzzing_spa_packets{$field}}) {
for my $encrypted_spa_pkt (@{$fuzzing_spa_packets{$field}{$field_val}}) {
### now get new object for decryption
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->encryption_mode(FKO->FKO_ENC_MODE_CBC_LEGACY_IV)
if $test_hr->{'set_legacy_iv'} eq $YES;
$fko_obj->spa_data($encrypted_spa_pkt);
my $status = $fko_obj->decrypt_spa_data($fuzzing_key, length($fuzzing_key));
if ($status == FKO->FKO_SUCCESS) {
&write_test_file("[-] Accepted fuzzing $field $field_val SPA packet.\n",
$curr_test_file);
$rv = 0;
$fuzzing_failure_ctr++;
} else {
&write_test_file("[+] Rejected fuzzing $field $field_val SPA packet.\n",
$curr_test_file);
$fuzzing_success_ctr++;
}
$fuzzing_ctr++;
$fko_obj->destroy();
}
}
}
return $rv;
}
sub perl_fko_module_client_compatibility() {
my $test_hr = shift;
$fko_obj = FKO->new();
unless ($fko_obj) {
&write_test_file("[-] error FKO->new(): " . FKO::error_str() . "\n",
$curr_test_file);
return 0;
}
$fko_obj->spa_message("$fake_ip,tcp/22");
$fko_obj->spa_message_type(FKO->FKO_ACCESS_MSG);
$fko_obj->encryption_mode(FKO->FKO_ENC_MODE_CBC_LEGACY_IV)
if $test_hr->{'set_legacy_iv'} eq $YES;
$fko_obj->spa_data_final($default_key, length($default_key), '', 0);
my $spa_pkt = $fko_obj->spa_data();
$fko_obj->destroy();
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;
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'},
$MATCH_ALL, $server_test_file);
}
return $rv;
}
sub get_mod_paths() {
my @paths = ();
opendir D, $perl_mod_fko_dir
or die "[*] Could not open $perl_mod_fko_dir: $!";
my @dirs = readdir D;
closedir D;
push @paths, $perl_mod_fko_dir;
for my $dir (@dirs) {
### get directories like "FKO/x86_64-linux"
next unless -d "$perl_mod_fko_dir/$dir";
push @paths, "$perl_mod_fko_dir/$dir"
if $dir =~ m|linux| or $dir =~ m|thread|
or (-d "$perl_mod_fko_dir/$dir/auto");
}
return \@paths;
}
sub spa_cmd_exec_cycle() {
my $test_hr = shift;
my $rv = &spa_cycle($test_hr);
if (-e $cmd_exec_test_file) {
unlink $cmd_exec_test_file;
} else {
$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($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n",
$curr_test_file);
return 0;
}
if ($test_hr->{'pkt_prefix'}) {
$spa_pkt = $test_hr->{'pkt_prefix'} . $spa_pkt;
}
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;
if ($test_hr->{'replay_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'replay_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
if ($test_hr->{'replay_negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'replay_negative_output_matches'},
$MATCH_ANY, $server_test_file);
}
return $rv;
}
sub digest_cache_structure() {
my $test_hr = shift;
my $rv = 1;
&run_cmd("file $default_digest_file", $cmd_out_tmp, $curr_test_file);
if (&file_find_regex([qr/ASCII/i], $MATCH_ALL, $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: $_",
$curr_test_file);
$rv = 0;
last;
}
}
close F;
} elsif (&file_find_regex([qr/dbm/i], $MATCH_ALL, $cmd_out_tmp)) {
&write_test_file("[+] DBM digest file format, " .
"assuming this is valid.\n", $curr_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", $curr_test_file);
$rv = 0;
}
if ($rv) {
&write_test_file("[+] valid digest.cache structure.\n",
$curr_test_file);
}
return $rv;
}
sub iptables_rules_not_duplicated() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
my @packets = ();
for (my $i=0; $i < 3; $i++) {
unless (&client_send_spa_packet($test_hr)) {
&write_test_file("[-] fwknop client execution error.\n",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($cmd_out_tmp);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_test_file);
return 0;
}
push @packets,
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $spa_pkt,
};
$i++;
}
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
### make sure there aren't two iptables rule with the same creation time
my $time_stamp = 0;
open F, "< $server_test_file" or die $!;
while (<F>) {
### 1 ACCEPT tcp -- 127.0.0.2 0.0.0.0/0 tcp dpt:22 /* _exp_1359688354 */
if (m|^\d+\s+.*$fake_ip\s+.*_exp_(\d+)|) {
$time_stamp = $1;
next;
}
if ($time_stamp) {
if (/^2\s+.*$fake_ip\s+.*_exp_$time_stamp/) {
$rv = 0;
last;
}
}
}
close F;
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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_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/],
$MATCH_ALL, $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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_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 backwards_compatibility() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $test_hr->{'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", $curr_test_file);
} else {
&write_test_file("[-] new fw rule not created.\n", $curr_test_file);
$rv = 0;
}
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
return $rv;
}
sub process_pcap_file_directly() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, [], $USE_PCAP_FILE);
$rv = 0 unless $server_was_stopped;
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'},
$MATCH_ALL, $server_test_file);
}
if ($test_hr->{'server_negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'server_negative_output_matches'},
$MATCH_ANY, $server_test_file);
}
return $rv;
}
sub fuzzer() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $test_hr->{'fuzzing_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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
if ($rv) {
$fuzzing_success_ctr++;
} else {
$fuzzing_failure_ctr++;
}
$fuzzing_ctr++;
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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$MATCH_ALL, $server_test_file)) {
$rv = 0;
}
return $rv;
}
sub altered_hmac_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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_test_file);
return 0;
}
### alter the HMAC region of the SPA packet
$spa_pkt =~ s|(.{5})$|AAAAA|;
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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$MATCH_ALL, $server_test_file)) {
$rv = 0;
}
return $rv;
}
sub altered_pkt_hmac_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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_test_file);
return 0;
}
### alter the SPA packet region before the HMAC
$spa_pkt =~ s|^(.{5})|AAAAA|;
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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$MATCH_ALL, $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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$MATCH_ALL, $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",
$curr_test_file);
$rv = 0;
}
my $spa_pkt = &get_spa_packet_from_file($curr_test_file);
unless ($spa_pkt) {
&write_test_file("[-] could not get SPA packet " .
"from file: $curr_test_file\n", $curr_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", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
$MATCH_ALL, $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/],
$MATCH_ALL, $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/],
$MATCH_ALL, $server_test_file)) {
$rv = 0;
}
unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
$MATCH_ALL, $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) = @_;
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",
$curr_test_file);
$rv = 0;
}
} elsif ($spa_client_flag == $USE_PREDEF_PKTS) {
&send_packets($pkts_hr);
} else {
### pcap file mode, nothing to do
}
### 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",
$curr_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",
$curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule timed out.\n",
$curr_test_file);
$fw_rule_removed = 1;
}
}
if (&is_fwknopd_running()) {
&stop_fwknopd();
unless (&file_find_regex([qr/Got\sSIGTERM/],
$MATCH_ALL, $server_test_file)) {
$server_was_stopped = 0;
}
} else {
&write_test_file("[-] server is not running.\n",
$curr_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, ">> $curr_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 rc_file_exists() {
my $test_hr = shift;
my $rv = 1;
if (-e $tmp_rc_file) {
$rv = 0 unless &file_find_regex([qr/This\sfile\scontains/],
$MATCH_ALL, $tmp_rc_file);
} else {
&write_test_file("[-] $tmp_rc_file does not exist.\n",
$curr_test_file);
$rv = 0;
}
return $rv;
}
sub generic_exec() {
my $test_hr = shift;
my $rv = 1;
my $exec_rv = &run_cmd($test_hr->{'cmdline'},
$cmd_out_tmp, $curr_test_file);
if ($test_hr->{'exec_err'} eq $YES) {
$rv = 0 if $exec_rv;
} elsif ($test_hr->{'exec_err'} eq $NO) {
$rv = 0 unless $exec_rv;
} ### else it must be $IGNORE so ignore the $exec_rv value
if ($test_hr->{'positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'positive_output_matches'},
$MATCH_ALL, $curr_test_file);
}
if ($test_hr->{'negative_output_matches'}) {
$rv = 0 if &file_find_regex(
$test_hr->{'negative_output_matches'},
$MATCH_ANY, $curr_test_file);
}
return $rv;
}
sub key_gen_uniqueness() {
my $test_hr = shift;
my %rijndael_keys = ();
my %hmac_keys = ();
### collect key information
my $found_dup = 0;
for (my $i=0; $i < $uniq_keys; $i++) {
open CMD, "$test_hr->{'cmdline'} | " or die $!;
while (<CMD>) {
if (/^KEY_BASE64\:\s+(\S+)/) {
$found_dup = 1 if defined $rijndael_keys{$1};
$rijndael_keys{$1} = '';
} elsif (/^HMAC_KEY_BASE64\:\s+(\S+)/) {
$found_dup = 1 if defined $hmac_keys{$1};
$hmac_keys{$1} = '';
}
}
close CMD;
last if $found_dup;
}
return ! $found_dup;
}
### check for PIE
sub pie_binary() {
my $test_hr = shift;
return 0 unless -e $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $curr_test_file);
return 1 if &file_find_regex([qr/Position\sIndependent.*:\syes/i],
$MATCH_ALL, $curr_test_file);
return 0;
}
### check for stack protection
sub stack_protected_binary() {
my $test_hr = shift;
return 0 unless -e $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $curr_test_file);
return 1 if &file_find_regex([qr/Stack\sprotected.*:\syes/i],
$MATCH_ALL, $curr_test_file);
return 0;
}
### check for fortified source functions
sub fortify_source_functions() {
my $test_hr = shift;
return 0 unless -e $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $curr_test_file);
return 1 if &file_find_regex([qr/Fortify\sSource\sfunctions:\syes/i],
$MATCH_ALL, $curr_test_file);
return 0;
}
### check for read-only relocations
sub read_only_relocations() {
my $test_hr = shift;
return 0 unless -e $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $curr_test_file);
return 1 if &file_find_regex([qr/Read.only\srelocations:\syes/i],
$MATCH_ALL, $curr_test_file);
return 0;
}
### check for immediate binding
sub immediate_binding() {
my $test_hr = shift;
return 0 unless -e $test_hr->{'binary'};
&run_cmd("./hardening-check $test_hr->{'binary'}",
$cmd_out_tmp, $curr_test_file);
return 1 if &file_find_regex([qr/Immediate\sbinding:\syes/i],
$MATCH_ALL, $curr_test_file);
return 0;
}
sub openssl_verification() {
my ($encrypted_msg, $encoded_msg, $access_msg, $key, $rv_flag) = @_;
my $rv = 1;
my $rv_str = 'REQUIRE_SUCCESS';
$rv_str = 'REQUIRE_FAILURE' if $rv_flag == $REQUIRE_FAILURE;
&write_test_file("[+] OpenSSL verification, (encoded msg: " .
"$encoded_msg) (access: $access_msg), key: $key, " .
"encrypted+encoded msg: $encrypted_msg, $rv_str\n",
$curr_test_file);
### transform encrypted message into the format that openssl expects
$encrypted_msg = 'U2FsdGVkX1' . $encrypted_msg
unless $encrypted_msg =~ /^U2FsdGVkX1/;
my $len_remainder = length($encrypted_msg) % 4;
if ($len_remainder > 0) {
for (my $i=0; $i < 4-$len_remainder; $i++) {
$encrypted_msg .= '=';
}
}
$encrypted_msg =~ s|(.{76})|$1\n|g;
open F, "> $data_tmp" or die $!;
print F $encrypted_msg, "\n";
close F;
open F, "> $key_tmp" or die $!;
print F $key;
close F;
$rv = &run_cmd("$openssl_path enc -d -a -aes-256-cbc " .
"-pass file:$key_tmp -in $data_tmp",
$cmd_out_tmp, $curr_test_file);
if ($rv) {
if ($rv_flag == $REQUIRE_FAILURE) {
&write_test_file("[.] OpenSSL decryption did not generate " .
"error code exit status\n",
$curr_test_file);
$rv = 0;
### make absolutely certain that the decrypted data does not contain
### a valid access message
my $decrypted_msg = '';
my $decrypted_access_msg = '';
open F, "< $cmd_out_tmp" or die $!;
while (<F>) {
if (/^(?:\S+?\:){5}(\S+?)\:/) {
$decrypted_access_msg = $1;
$decrypted_msg = $_;
}
}
close F;
if ($decrypted_msg) {
if ($encoded_msg and $encoded_msg eq $decrypted_msg) {
&write_test_file("[-] OpenSSL DECRYPTED msg with truncated key!\n",
$curr_test_file);
$rv = 1;
}
}
if ($decrypted_access_msg) {
my $decoded_msg = decode_base64($decrypted_access_msg);
if ($decoded_msg) {
if ($access_msg and $access_msg eq $decoded_msg) {
&write_test_file("[-] OpenSSL DECRYPTED msg with truncated key!\n",
$curr_test_file);
$rv = 1;
}
}
}
} else {
### 2868244741993914:dGVzdA:2358972093:2.0.4:1:MS4yLjMANCx0YAAvMjI:vPFqXEA6SnzP2ScsIWAxhg
### make sure the access message checks out, or the entire
### decrypted (but not decoded) packet if we were passed the
### encoded version
my $decrypted_msg = '';
my $decrypted_access_msg = '';
my $decoded_msg = '';
open F, "< $cmd_out_tmp" or die $!;
while (<F>) {
if (/^(?:\S+?\:){5}(\S+?)\:/) {
$decrypted_access_msg = $1;
$decrypted_msg = $_;
}
}
close F;
$decrypted_msg =~ s/\n//;
my $decryption_success = 0;
unless ($encoded_msg) {
my $len_remainder = length($decrypted_access_msg) % 4;
if ($len_remainder > 0) {
for (my $i=0; $i < 4-$len_remainder; $i++) {
$decrypted_access_msg .= '=';
}
}
$decoded_msg = decode_base64($decrypted_access_msg);
}
if ($encoded_msg) {
$decryption_success = 1 if $encoded_msg eq $decrypted_msg;
} else {
$decryption_success = 1 if $access_msg eq $decoded_msg;
}
if ($decryption_success) {
&write_test_file("[+] OpenSSL access message " .
"match in decrypted data\n",
$curr_test_file);
### now check the exit status of re-encrypting the data
unless (&run_cmd("$openssl_path enc " .
"-e -a -aes-256-cbc -pass file:$key_tmp -in " .
"$data_tmp -out $enc_save_tmp",
$cmd_out_tmp, $curr_test_file)) {
&write_test_file("[-] OpenSSL could not re-encrypt\n",
$curr_test_file);
$rv = 0;
}
} else {
&write_test_file("[-] OpenSSL access message " .
"mis-match in decrypted data\n",
$curr_test_file);
$rv = 0;
}
}
} else {
if ($rv_flag == $REQUIRE_SUCCESS) {
&write_test_file("[-] OpenSSL bad decryption exit status\n",
$curr_test_file);
} else {
&write_test_file("[+] OpenSSL did not decrypt bogus " .
"key/data combination\n",
$curr_test_file);
}
}
if ($rv) {
if ($rv_flag == $REQUIRE_SUCCESS) {
&write_test_file("[+] OpenSSL test success (expected " .
"encryption/decryption success)\n",
$curr_test_file);
$openssl_success_ctr++;
} else {
&write_test_file("[-] OpenSSL test failure (expected " .
"encryption/decryption failure)\n",
$curr_test_file);
$openssl_failure_ctr++;
$rv = 0;
}
} else {
if ($rv_flag == $REQUIRE_SUCCESS) {
&write_test_file("[-] OpenSSL test failure (expected " .
"encryption/decryption success)\n",
$curr_test_file);
$openssl_failure_ctr++;
} else {
&write_test_file("[+] OpenSSL test success (expected " .
"encryption/decryption failure)\n",
$curr_test_file);
$openssl_success_ctr++;
$rv = 1;
}
}
$openssl_ctr++;
return $rv;
}
sub specs() {
&run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
"$default_server_conf_args --fw-list-all",
$cmd_out_tmp, $curr_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, $curr_test_file);
if ($cmd =~ /^ldd/) {
$have_gpgme++ if &file_find_regex([qr/gpgme/],
$MATCH_ALL, $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, qr/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, $curr_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: $!";
}
print "[+] Anonymizing all IP addresses and hostnames ",
"from $output_dir files...\n";
### 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
print " Creating tar file: $tarfile\n";
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, ">> $curr_test_file"
or die "[*] Could not open $curr_test_file: $!";
print C "\n" . localtime() . " [+] PID dump:\n";
close C;
&run_cmd("ps auxww | grep knop |grep -v grep",
$cmd_out_tmp, $curr_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;
}
### copy original file descriptors (credit: Perl Cookbook)
open OLDOUT, ">&STDOUT";
open OLDERR, ">&STDERR";
### redirect command output
open STDOUT, "> $cmd_out" or die "[*] Could not redirect stdout: $!";
open STDERR, ">&STDOUT" or die "[*] Could not dup stdout: $!";
my $rv = ((system $cmd) >> 8);
close STDOUT or die "[*] Could not close STDOUT: $!";
close STDERR or die "[*] Could not close STDERR: $!";
### restore original filehandles
open STDERR, ">&OLDERR" or die "[*] Could not restore stderr: $!";
open STDOUT, ">&OLDOUT" or die "[*] Could not restore stdout: $!";
### close the old copies
close OLDOUT or die "[*] Could not close OLDOUT: $!";
close OLDERR or die "[*] Could not close OLDERR: $!";
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: $!";
for (@cmd_lines) {
if (/\n/) {
print F $_;
} else {
print F $_, "\n";
}
}
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;
unless ($enable_recompilation_warnings_check) {
die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
}
unlink $cmd_exec_test_file if -e $cmd_exec_test_file;
for my $name (keys %cf) {
die "[*] $cf{$name} does not exist" unless -e $cf{$name};
chmod 0600, $cf{$name} or die "[*] Could not chmod 0600 $cf{$name}";
}
if (-d $output_dir) {
if (-d "${output_dir}.last") {
rmtree "${output_dir}.last"
or die "[*] rmtree ${output_dir}.last $!";
}
move $output_dir, "${output_dir}.last" 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: $!";
}
if (-d $run_dir) {
rmtree $run_dir or die $!;
}
mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
for my $dir ($output_dir, $run_dir) {
next if -d $dir;
mkdir $dir or die "[*] Could not mkdir $dir: $!";
}
for my $file (glob("$output_dir/*.test"), "$output_dir/init",
$tmp_rc_file, $logfile, $key_gen_file) {
next unless -e $file;
unlink $file or die "[*] Could not unlink($file)";
}
if ($test_include) {
for my $re (split /\s*,\s*/, $test_include) {
push @tests_to_include, qr/$re/;
}
}
if ($test_exclude) {
for my $re (split /\s*,\s*/, $test_exclude) {
push @tests_to_exclude, qr/$re/;
}
}
### make sure test message strings are unique across all tests
my %uniq_test_msgs = ();
for my $test_hr (@tests) {
my $msg = "[$test_hr->{'category'}]";
$msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
$msg .= " $test_hr->{'detail'}";
if (defined $uniq_test_msgs{$msg}) {
die "[*] Duplicate test message: $msg\n";
} else {
$uniq_test_msgs{$msg} = '';
}
}
### make sure no fwknopd instance is currently running
die "[*] Please stop the running fwknopd instance."
if &is_fwknopd_running();
if ($enable_all) {
$enable_recompilation_warnings_check = 1;
$enable_make_distcheck = 1;
$enable_client_ip_resolve_test = 1;
$enable_perl_module_checks = 1;
$enable_openssl_compatibility_tests = 1;
}
if ($enable_openssl_compatibility_tests) {
$openssl_path = &find_command('openssl');
die "[*] openssl command not found." unless $openssl_path;
require MIME::Base64;
MIME::Base64->import(qw(decode_base64));
}
$enable_perl_module_checks = 1
if $enable_perl_module_fuzzing_spa_pkt_generation;
if ($fuzzing_test_tag) {
$fuzzing_test_tag .= '_' unless $fuzzing_test_tag =~ /_$/;
}
unless ($enable_recompilation_warnings_check) {
push @tests_to_exclude, qr/recompilation/;
}
unless ($enable_make_distcheck) {
push @tests_to_exclude, qr/distcheck/;
}
unless ($enable_profile_coverage_check) {
push @tests_to_exclude, qr/profile coverage/;
}
unless ($enable_client_ip_resolve_test) {
push @tests_to_exclude, qr/IP resolve/;
}
if ($enable_perl_module_checks) {
open F, "< $fuzzing_pkts_file" or die $!;
while (<F>) {
if (/(?:Bogus|Invalid_encoding)\s(\S+)\:\s+(.*)\,\sSPA\spacket\:\s(\S+)/) {
push @{$fuzzing_spa_packets{$1}{$2}}, $3;
$total_fuzzing_pkts++;
}
}
close F;
} else {
push @tests_to_exclude, qr/perl FKO module/;
}
if ($enable_perl_module_fuzzing_spa_pkt_generation) {
push @tests_to_include, qr/perl FKO module/;
if ($fuzzing_class eq 'bogus data') {
push @tests_to_exclude, qr/perl FKO module.*FUZZING.*invalid encoded/;
} else {
push @tests_to_exclude, qr/perl FKO module.*FUZZING.*invalid SPA/;
}
} else {
push @tests_to_exclude, qr/perl FKO module.*FUZZING/;
}
$sudo_path = &find_command('sudo');
$killall_path = &find_command('killall');
unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
### disable compilation checks
push @tests_to_exclude, qr/recompilation/;
}
$gcov_path = &find_command('gcov');
if ($gcov_path) {
if ($enable_profile_coverage_check) {
for my $extension ('*.gcov', '*.gcda') {
### remove profile output from any previous run
system qq{find .. -name $extension | xargs rm 2> /dev/null};
}
}
} else {
push @tests_to_exclude, qr/profile coverage/;
}
open UNAME, "uname |" or die "[*] Could not execute uname: $!";
while (<UNAME>) {
if (/linux/i) {
$platform = $LINUX;
last;
} elsif (/freebsd/i) {
$platform = $FREEBSD;
last;
}
}
close UNAME;
unless ($platform eq $LINUX) {
push @tests_to_exclude, qr/NAT/;
push @tests_to_exclude, qr/iptables/;
}
unless ($platform eq $FREEBSD or $platform eq $MACOSX) {
push @tests_to_exclude, qr|active/expire sets|;
}
for my $file ($default_digest_file, "${default_digest_file}-old") {
if (-e $file) {
unlink $file;
}
}
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 import_previous_valgrind_coverage_info() {
if ($previous_valgrind_coverage_dir) {
die "[*] $previous_valgrind_coverage_dir does not exist"
unless -d $previous_valgrind_coverage_dir;
} else {
### try the previous output.last/valgrind-coverage dir first
$previous_valgrind_coverage_dir = "${output_dir}.last/$valgrind_cov_dir";
unless (-d $previous_valgrind_coverage_dir) {
my $os = 'linux';
$os = 'freebsd' if $platform == $FREEBSD;
$previous_valgrind_coverage_dir = "valgrind-coverage/$os";
}
}
return unless -d $previous_valgrind_coverage_dir;
for my $file (glob("$previous_valgrind_coverage_dir/*.test")) {
my $test_title = '';
my $type = 'server';
$type = 'client' if $file =~ /\d\.test/;
my $found = 0;
open F, "< $file" or die $!;
while (<F>) {
if (/TEST\:\s/) {
$test_title = $_;
chomp $test_title;
next;
}
### stop after the unique functions view
last if /with\scall\sline\snumbers/;
if ($test_title) {
if (/^\s+(\d+)\s\:\s(.*)/) {
$prev_valgrind_cov{$type}{$test_title}{$2} = $1;
$found = 1;
}
}
}
close F;
unless ($found) {
$prev_valgrind_cov{$type}{$test_title}{'NO_FLAGGED_FCNS'} = '';
}
}
return;
}
sub parse_valgrind_flagged_functions() {
my $rv = 1;
&import_previous_valgrind_coverage_info();
if (%prev_valgrind_cov) {
&write_test_file("[+] Imported previous valgrind data from: " .
"$previous_valgrind_coverage_dir\n", $curr_test_file);
} else {
if (-d $previous_valgrind_coverage_dir) {
&write_test_file("[-] Did not import previous valgrind data " .
"from: $previous_valgrind_coverage_dir\n", $curr_test_file);
} else {
&write_test_file("[-] Previous valgrind data dir does not exist: " .
"from: $previous_valgrind_coverage_dir\n", $curr_test_file);
}
}
mkdir "$output_dir/$valgrind_cov_dir"
unless -d "$output_dir/$valgrind_cov_dir";
for my $file (glob("$output_dir/*.test")) {
my $type = 'server';
$type = 'client' if $file =~ /\d\.test/;
my $filename = $1 if $file =~ m|.*/(.*)|;
my %file_scope_flagged_fcns = ();
my %file_scope_flagged_fcns_unique = ();
my $test_title = '';
open F, "< $file" or die $!;
while (<F>) {
### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
$valgrind_flagged_fcns{$type}{"$1 $2"}++;
$valgrind_flagged_fcns_unique{$type}{$1}++;
$file_scope_flagged_fcns{"$1 $2"}++;
$file_scope_flagged_fcns_unique{$1}++;
} elsif (/TEST\:\s/) {
$test_title = $_;
chomp $test_title;
}
}
close F;
next if $test_title =~ /valgrind\soutput/;
### write out flagged fcns for this file
if ($filename) {
open F, "> $output_dir/$valgrind_cov_dir/$filename"
or die "[*] Could not open file $output_dir/$valgrind_cov_dir/$filename: $!";
print F $test_title, "\n";
if (keys %file_scope_flagged_fcns_unique) {
print F "\n[+] fwknop functions (unique view):\n";
for my $fcn (sort {$file_scope_flagged_fcns_unique{$b}
<=> $file_scope_flagged_fcns_unique{$a}}
keys %file_scope_flagged_fcns_unique) {
printf F " %5d : %s\n", $file_scope_flagged_fcns_unique{$fcn}, $fcn;
}
}
if (keys %file_scope_flagged_fcns) {
print F "\n[+] fwknop functions (with call line numbers):\n";
for my $fcn (sort {$file_scope_flagged_fcns{$b}
<=> $file_scope_flagged_fcns{$a}} keys %file_scope_flagged_fcns) {
printf F " %5d : %s\n", $file_scope_flagged_fcns{$fcn}, $fcn;
}
}
close F;
my $new_flagged_fcns = 0;
### look for differences in flagged functions between the two
### test runs
for my $fcn (sort {$file_scope_flagged_fcns_unique{$b}
<=> $file_scope_flagged_fcns_unique{$a}}
keys %file_scope_flagged_fcns_unique) {
if (defined $prev_valgrind_cov{$type}
and defined $prev_valgrind_cov{$type}{$test_title}) {
### we're looking at a matching test results file at this point
if (defined $prev_valgrind_cov{$type}{$test_title}{$fcn}) {
my $prev_calls = $prev_valgrind_cov{$type}{$test_title}{$fcn};
my $curr_calls = $file_scope_flagged_fcns_unique{$fcn};
if ($curr_calls > $prev_calls) {
open F, ">> $curr_test_file" or die $!;
print F "[-] $filename ($type) '$test_title' --> Larger number of flagged calls to " .
"$fcn (current: $curr_calls, previous: $prev_calls)\n";
close F;
$new_flagged_fcns = 1;
}
} else {
open F, ">> $curr_test_file" or die $!;
print F "[-] $filename ($type) '$test_title' --> NEW valgrind flagged function: $fcn\n";
close F;
$new_flagged_fcns = 1;
}
}
}
if ($new_flagged_fcns) {
open F, ">> $curr_test_file" or die $!;
print F "[-] $filename New and/or greater number of valgrind flagged function calls\n";
close F;
$rv = 0;
} else {
open F, ">> $curr_test_file" or die $!;
print F "[+] $filename No new or greater number of valgrind flagged function calls\n";
close F;
}
}
}
open F, ">> $curr_test_file" or die $!;
for my $type ('client', 'server') {
print F "\n[+] fwknop $type functions (unique view):\n";
next unless defined $valgrind_flagged_fcns_unique{$type};
for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
<=> $valgrind_flagged_fcns_unique{$type}{$a}}
keys %{$valgrind_flagged_fcns_unique{$type}}) {
printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
}
print F "\n[+] fwknop $type functions (with call line numbers):\n";
for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
<=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
}
next unless defined $valgrind_flagged_fcns{$type};
}
close F;
return $rv;
}
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 $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file";
}
if ($test_hr->{'no_ip_check'}) {
return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
qq{$conf_args --fw-list | grep -v "# DISABLED" |grep _exp_},
$cmd_out_tmp, $curr_test_file);
} else {
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, $curr_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, $curr_test_file);
return 1 if &file_find_regex([qr/Detected\sfwknopd\sis\srunning/i],
$MATCH_ALL, $cmd_out_tmp);
return 0;
}
sub stop_fwknopd() {
&run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
"$default_server_conf_args -K", $cmd_out_tmp, $curr_test_file);
if ($use_valgrind) {
&time_for_valgrind();
} else {
sleep 1;
}
return;
}
sub file_find_regex() {
my ($re_ar, $match_style, $file) = @_;
my $found_all_regexs = 1;
my $found_single_match = 0;
my @write_lines = ();
my @file_lines = ();
open F, "< $file" or die "[*] Could not open $file: $!";
while (<F>) {
push @file_lines, $_;
}
close F;
for my $re (@$re_ar) {
my $matched = 0;
for my $line (@file_lines) {
if ($line =~ $re) {
push @write_lines, "[.] file_find_regex() " .
"Matched '$re' with line: $line";
$matched = 1;
$found_single_match = 1;
}
}
unless ($matched) {
push @write_lines, "[.] file_find_regex() " .
"Did not match regex '$re' from regexs: '@$re_ar' " .
"within file: $file\n";
$found_all_regexs = 0;
}
}
for my $line (@write_lines) {
&write_test_file($line, $file);
}
if ($match_style == $MATCH_ANY) {
return $found_single_match;
}
return $found_all_regexs;
}
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;
}
sub usage() {
print <<_HELP_;
[+] $0 <options>
-A --Anonymize-results - Prepare anonymized results at:
$tarfile
--diff - Compare the results of one test run to
another. By default this compares output
in ${output_dir}.last to $output_dir
--diff-dir1=<path> - Left hand side of diff directory path,
default is: ${output_dir}.last
--diff-dir2=<path> - Right hand side of diff directory path,
default is: $output_dir
--include=<regex> - Specify a regex to be used over test
names that must match.
--exclude=<regex> - Specify a regex to be used over test
names that must not match.
--enable-recompile - Recompile fwknop sources and look for
compilation warnings.
--enable-valgrind - Run every test underneath valgrind.
--enable-ip-resolve - Enable client IP resolution (-R) test -
this requires internet access.
--enable-distcheck - Enable 'make dist' check.
--enable-perl-module-checks - Run a series of tests against libfko via
the perl FKO module.
--enable-perl-module-pkt-gen - Generate a series of fuzzing packets via
the perl FKO module (assumes a patched
libfko code to accept fuzzing values).
The generated packets are placed in:
$fuzzing_pkts_file
--enable-openssl-checks - Enable tests to verify that Rijndael
cipher usage is compatible with openssl.
--enable-all - Enable tests that aren't enabled by
default, except that --enable-valgrind
must also be set if valgrind mode is
desired.
--fuzzing-pkts-file <file> - Specify path to fuzzing packet file.
--fuzzing-pkts-append - When generating new fuzzing packets,
append them to the fuzzing packets file.
--List - List test names.
--test-limit=<num> - Limit the number of tests that will run.
--loopback-intf=<intf> - Specify loopback interface name (default
depends on the OS where the test suite
is executed).
--output-dir=<path> - Path to output directory, default is:
$output_dir
--fwknop-path=<path> - Path to fwknop binary, default is:
$fwknopCmd
--fwknopd-path=<path> - Path to fwknopd binary, default is:
$fwknopdCmd
--libfko-path=<path> - Path to libfko, default is:
$libfko_bin
--valgrind-path=<path> - Path to valgrind, default is:
$valgrindCmd
--valgrind-prev-cov-dir=<path> - Path to previous valgrind-coverage
directory (can set to
"output.last/valgrind-coverage").
-h --help - Display usage on STDOUT and exit.
_HELP_
exit 0;
}