b678d31647526e11552ad505b055605e2cdac090
[fwknop.git] / test / test-fwknop.pl
1 #!/usr/bin/perl -w
2
3 use File::Copy;
4 use File::Path;
5 use IO::Socket;
6 use Data::Dumper;
7 use Getopt::Long 'GetOptions';
8 use strict;
9
10 #==================== config =====================
11 my $logfile        = 'test.log';
12 my $local_key_file = 'local_spa.key';
13 my $output_dir     = 'output';
14 my $lib_dir        = '../lib/.libs';
15 my $conf_dir       = 'conf';
16 my $run_dir        = 'run';
17 my $configure_path = '../configure';
18 my $cmd_out_tmp    = 'cmd.out';
19 my $server_cmd_tmp = 'server_cmd.out';
20 my $gpg_client_home_dir = "$conf_dir/client-gpg";
21 my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw";
22
23 my %cf = (
24     'nat'                     => "$conf_dir/nat_fwknopd.conf",
25     'def'                     => "$conf_dir/default_fwknopd.conf",
26     'def_access'              => "$conf_dir/default_access.conf",
27     'exp_access'              => "$conf_dir/expired_stanza_access.conf",
28     'future_exp_access'       => "$conf_dir/future_expired_stanza_access.conf",
29     'exp_epoch_access'        => "$conf_dir/expired_epoch_stanza_access.conf",
30     'invalid_exp_access'      => "$conf_dir/invalid_expire_access.conf",
31     'force_nat_access'        => "$conf_dir/force_nat_access.conf",
32     'local_nat'               => "$conf_dir/local_nat_fwknopd.conf",
33     'ipfw_active_expire'      => "$conf_dir/ipfw_active_expire_equal_fwknopd.conf",
34     'dual_key_access'         => "$conf_dir/dual_key_usage_access.conf",
35     'gpg_access'              => "$conf_dir/gpg_access.conf",
36     'gpg_no_pw_access'        => "$conf_dir/gpg_no_pw_access.conf",
37     'open_ports_access'       => "$conf_dir/open_ports_access.conf",
38     'multi_gpg_access'        => "$conf_dir/multi_gpg_access.conf",
39     'multi_stanza_access'     => "$conf_dir/multi_stanzas_access.conf",
40     'broken_keys_access'      => "$conf_dir/multi_stanzas_with_broken_keys.conf",
41     'open_ports_mismatch'     => "$conf_dir/mismatch_open_ports_access.conf",
42     'require_user_access'     => "$conf_dir/require_user_access.conf",
43     'user_mismatch_access'    => "$conf_dir/mismatch_user_access.conf",
44     'require_src_access'      => "$conf_dir/require_src_access.conf",
45     'no_src_match'            => "$conf_dir/no_source_match_access.conf",
46     'no_subnet_match'         => "$conf_dir/no_subnet_source_match_access.conf",
47     'no_multi_src'            => "$conf_dir/no_multi_source_match_access.conf",
48     'multi_src_access'        => "$conf_dir/multi_source_match_access.conf",
49     'ip_src_match'            => "$conf_dir/ip_source_match_access.conf",
50     'subnet_src_match'        => "$conf_dir/ip_source_match_access.conf",
51     'disable_aging'           => "$conf_dir/disable_aging_fwknopd.conf",
52     'fuzz_source'             => "$conf_dir/fuzzing_source_access.conf",
53     'fuzz_open_ports'         => "$conf_dir/fuzzing_open_ports_access.conf",
54     'fuzz_restrict_ports'     => "$conf_dir/fuzzing_restrict_ports_access.conf",
55 );
56
57 my $default_digest_file = "$run_dir/digest.cache";
58 my $default_pid_file    = "$run_dir/fwknopd.pid";
59
60 my $fwknopCmd   = '../client/.libs/fwknop';
61 my $fwknopdCmd  = '../server/.libs/fwknopd';
62 my $libfko_bin  = "$lib_dir/libfko.so";  ### this is usually a link
63 my $valgrindCmd = '/usr/bin/valgrind';
64
65 my $gpg_server_key = '361BBAD4';
66 my $gpg_client_key = '6A3FAD56';
67
68 my $loopback_ip = '127.0.0.1';
69 my $fake_ip     = '127.0.0.2';
70 my $internal_nat_host = '192.168.1.2';
71 my $force_nat_host = '192.168.1.123';
72 my $default_spa_port = 62201;
73 my $non_std_spa_port = 12345;
74
75 my $spoof_user = 'testuser';
76 #================== end config ===================
77
78 my $passed = 0;
79 my $failed = 0;
80 my $executed = 0;
81 my $test_include = '';
82 my @tests_to_include = ();
83 my $test_exclude = '';
84 my @tests_to_exclude = ();
85 my %valgrind_flagged_fcns = ();
86 my %valgrind_flagged_fcns_unique = ();
87 my $list_mode = 0;
88 my $diff_dir1 = '';
89 my $diff_dir2 = '';
90 my $loopback_intf = '';
91 my $anonymize_results = 0;
92 my $current_test_file = "$output_dir/init";
93 my $tarfile = 'test_fwknop.tar.gz';
94 my $server_test_file  = '';
95 my $use_valgrind = 0;
96 my $valgrind_str = '';
97 my $enable_client_ip_resolve_test = 0;
98 my $saved_last_results = 0;
99 my $diff_mode = 0;
100 my $enable_recompilation_warnings_check = 0;
101 my $enable_make_distcheck = 0;
102 my $sudo_path = '';
103 my $platform = '';
104 my $help = 0;
105 my $YES = 1;
106 my $NO  = 0;
107 my $PRINT_LEN = 68;
108 my $USE_PREDEF_PKTS = 1;
109 my $USE_CLIENT = 2;
110 my $REQUIRED = 1;
111 my $OPTIONAL = 0;
112 my $NEW_RULE_REQUIRED = 1;
113 my $REQUIRE_NO_NEW_RULE = 2;
114 my $NEW_RULE_REMOVED = 1;
115 my $REQUIRE_NO_NEW_REMOVED = 2;
116 my $MATCH_ANY = 1;
117 my $MATCH_ALL = 2;
118 my $LINUX   = 1;
119 my $FREEBSD = 2;
120 my $MACOSX  = 3;
121 my $OPENBSD = 4;
122
123 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|;  ### IPv4
124
125 my @args_cp = @ARGV;
126
127 exit 1 unless GetOptions(
128     'Anonymize-results' => \$anonymize_results,
129     'fwknop-path=s'     => \$fwknopCmd,
130     'fwknopd-path=s'    => \$fwknopdCmd,
131     'libfko-path=s'     => \$libfko_bin,
132     'loopback-intf=s'   => \$loopback_intf,
133     'test-include=s'    => \$test_include,
134     'include=s'         => \$test_include,  ### synonym
135     'test-exclude=s'    => \$test_exclude,
136     'exclude=s'         => \$test_exclude,  ### synonym
137     'enable-recompile-check' => \$enable_recompilation_warnings_check,
138     'enable-ip-resolve' => \$enable_client_ip_resolve_test,
139     'enable-distcheck'  => \$enable_make_distcheck,
140     'List-mode'         => \$list_mode,
141     'enable-valgrind'   => \$use_valgrind,
142     'valgrind-path=s'   => \$valgrindCmd,
143     'output-dir=s'      => \$output_dir,
144     'diff'              => \$diff_mode,
145     'diff-dir1=s'       => \$diff_dir1,
146     'diff-dir2=s'       => \$diff_dir2,
147     'help'              => \$help
148 );
149
150 &usage() if $help;
151
152 ### create an anonymized tar file of test suite results that can be
153 ### emailed around to assist in debugging fwknop communications
154 exit &anonymize_results() if $anonymize_results;
155
156 &identify_loopback_intf();
157
158 $valgrind_str = "$valgrindCmd --leak-check=full " .
159     "--show-reachable=yes --track-origins=yes" if $use_valgrind;
160
161 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
162
163 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
164     "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
165     "$local_key_file --verbose --verbose";
166
167 my $client_ip_resolve_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
168     "$fwknopCmd -A tcp/22 -R -D $loopback_ip --get-key " .
169     "$local_key_file --verbose --verbose";
170
171 my $default_client_gpg_args = "$default_client_args " .
172     "--gpg-recipient-key $gpg_server_key " .
173     "--gpg-signer-key $gpg_client_key " .
174     "--gpg-home-dir $gpg_client_home_dir";
175
176 my $default_client_gpg_args_no_homedir = "$default_client_args " .
177     "--gpg-recipient-key $gpg_server_key " .
178     "--gpg-signer-key $gpg_client_key ";
179
180 my $default_server_conf_args = "-c $cf{'def'} -a $cf{'def_access'} " .
181     "-d $default_digest_file -p $default_pid_file";
182
183 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
184     "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
185     "-a $cf{'gpg_access'} $intf_str " .
186     "-d $default_digest_file -p $default_pid_file";
187
188 my $default_server_gpg_args_no_pw = "LD_LIBRARY_PATH=$lib_dir " .
189     "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
190     "-a $cf{'gpg_no_pw_access'} $intf_str " .
191     "-d $default_digest_file -p $default_pid_file";
192
193 ### point the compiled binaries at the local libary path
194 ### instead of any installed libfko instance
195 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
196
197 ### main array that defines the tests we will run
198 my @tests = (
199     {
200         'category' => 'recompilation',
201         'detail'   => 'recompile and look for compilation warnings',
202         'err_msg'  => 'compile warnings exist',
203         'function' => \&compile_warnings,
204         'fatal'    => $NO
205     },
206     {
207         'category' => 'make distcheck',
208         'detail'   => 'ensure proper distribution creation',
209         'err_msg'  => 'could not create proper tarball',
210         'function' => \&make_distcheck,
211         'fatal'    => $NO
212     },
213     {
214         'category' => 'build',
215         'subcategory' => 'client',
216         'detail'   => 'binary exists',
217         'err_msg'  => 'binary not found',
218         'function' => \&binary_exists,
219         'binary'   => $fwknopCmd,
220         'fatal'    => $YES
221     },
222     {
223         'category' => 'build security',
224         'subcategory' => 'client',
225         'detail'   => 'Position Independent Executable (PIE)',
226         'err_msg'  => 'non PIE binary (fwknop client)',
227         'function' => \&pie_binary,
228         'binary'   => $fwknopCmd,
229         'fatal'    => $NO
230     },
231     {
232         'category' => 'build security',
233         'subcategory' => 'client',
234         'detail'   => 'stack protected binary',
235         'err_msg'  => 'non stack protected binary (fwknop client)',
236         'function' => \&stack_protected_binary,
237         'binary'   => $fwknopCmd,
238         'fatal'    => $NO
239     },
240     {
241         'category' => 'build security',
242         'subcategory' => 'client',
243         'detail'   => 'fortify source functions',
244         'err_msg'  => 'source functions not fortified (fwknop client)',
245         'function' => \&fortify_source_functions,
246         'binary'   => $fwknopCmd,
247         'fatal'    => $NO
248     },
249     {
250         'category' => 'build security',
251         'subcategory' => 'client',
252         'detail'   => 'read-only relocations',
253         'err_msg'  => 'no read-only relocations (fwknop client)',
254         'function' => \&read_only_relocations,
255         'binary'   => $fwknopCmd,
256         'fatal'    => $NO
257     },
258     {
259         'category' => 'build security',
260         'subcategory' => 'client',
261         'detail'   => 'immediate binding',
262         'err_msg'  => 'no immediate binding (fwknop client)',
263         'function' => \&immediate_binding,
264         'binary'   => $fwknopCmd,
265         'fatal'    => $NO
266     },
267
268     {
269         'category' => 'build',
270         'subcategory' => 'server',
271         'detail'   => 'binary exists',
272         'err_msg'  => 'binary not found',
273         'function' => \&binary_exists,
274         'binary'   => $fwknopdCmd,
275         'fatal'    => $YES
276     },
277
278     {
279         'category' => 'build security',
280         'subcategory' => 'server',
281         'detail'   => 'Position Independent Executable (PIE)',
282         'err_msg'  => 'non PIE binary (fwknopd server)',
283         'function' => \&pie_binary,
284         'binary'   => $fwknopdCmd,
285         'fatal'    => $NO
286     },
287     {
288         'category' => 'build security',
289         'subcategory' => 'server',
290         'detail'   => 'stack protected binary',
291         'err_msg'  => 'non stack protected binary (fwknopd server)',
292         'function' => \&stack_protected_binary,
293         'binary'   => $fwknopdCmd,
294         'fatal'    => $NO
295     },
296     {
297         'category' => 'build security',
298         'subcategory' => 'server',
299         'detail'   => 'fortify source functions',
300         'err_msg'  => 'source functions not fortified (fwknopd server)',
301         'function' => \&fortify_source_functions,
302         'binary'   => $fwknopdCmd,
303         'fatal'    => $NO
304     },
305     {
306         'category' => 'build security',
307         'subcategory' => 'server',
308         'detail'   => 'read-only relocations',
309         'err_msg'  => 'no read-only relocations (fwknopd server)',
310         'function' => \&read_only_relocations,
311         'binary'   => $fwknopdCmd,
312         'fatal'    => $NO
313     },
314     {
315         'category' => 'build security',
316         'subcategory' => 'server',
317         'detail'   => 'immediate binding',
318         'err_msg'  => 'no immediate binding (fwknopd server)',
319         'function' => \&immediate_binding,
320         'binary'   => $fwknopdCmd,
321         'fatal'    => $NO
322     },
323
324     {
325         'category' => 'build',
326         'subcategory' => 'libfko',
327         'detail'   => 'binary exists',
328         'err_msg'  => 'binary not found',
329         'function' => \&binary_exists,
330         'binary'   => $libfko_bin,
331         'fatal'    => $YES
332     },
333     {
334         'category' => 'build security',
335         'subcategory' => 'libfko',
336         'detail'   => 'stack protected binary',
337         'err_msg'  => 'non stack protected binary (libfko)',
338         'function' => \&stack_protected_binary,
339         'binary'   => $libfko_bin,
340         'fatal'    => $NO
341     },
342     {
343         'category' => 'build security',
344         'subcategory' => 'libfko',
345         'detail'   => 'fortify source functions',
346         'err_msg'  => 'source functions not fortified (libfko)',
347         'function' => \&fortify_source_functions,
348         'binary'   => $libfko_bin,
349         'fatal'    => $NO
350     },
351     {
352         'category' => 'build security',
353         'subcategory' => 'libfko',
354         'detail'   => 'read-only relocations',
355         'err_msg'  => 'no read-only relocations (libfko)',
356         'function' => \&read_only_relocations,
357         'binary'   => $libfko_bin,
358         'fatal'    => $NO
359     },
360     {
361         'category' => 'build security',
362         'subcategory' => 'libfko',
363         'detail'   => 'immediate binding',
364         'err_msg'  => 'no immediate binding (libfko)',
365         'function' => \&immediate_binding,
366         'binary'   => $libfko_bin,
367         'fatal'    => $NO
368     },
369
370     {
371         'category' => 'preliminaries',
372         'subcategory' => 'client',
373         'detail'   => 'usage info',
374         'err_msg'  => 'could not get usage info',
375         'function' => \&generic_exec,
376         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
377         'fatal'    => $NO
378     },
379     {
380         'category' => 'preliminaries',
381         'subcategory' => 'client',
382         'detail'   => 'getopt() no such argument',
383         'err_msg'  => 'getopt() allowed non-existant argument',
384         'function' => \&generic_exec,
385         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
386         'exec_err' => $YES,
387         'fatal'    => $NO
388     },
389     {
390         'category' => 'preliminaries',
391         'subcategory' => 'client',
392         'detail'   => '--test mode, packet not sent',
393         'err_msg'  => '--test mode, packet sent?',
394         'function' => \&generic_exec,
395         'positive_output_matches' => [qr/test\smode\senabled/],
396         'cmdline'  => "$default_client_args --test",
397         'fatal'    => $NO
398     },
399
400     {
401         'category' => 'preliminaries',
402         'subcategory' => 'client',
403         'detail'   => 'expected code version',
404         'err_msg'  => 'code version mis-match',
405         'function' => \&expected_code_version,
406         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
407         'fatal'    => $NO
408     },
409
410     {
411         'category' => 'preliminaries',
412         'subcategory' => 'server',
413         'detail'   => 'usage info',
414         'err_msg'  => 'could not get usage info',
415         'function' => \&generic_exec,
416         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
417         'fatal'    => $NO
418     },
419     {
420         'category' => 'preliminaries',
421         'subcategory' => 'server',
422         'detail'   => 'getopt() no such argument',
423         'err_msg'  => 'getopt() allowed non-existant argument',
424         'function' => \&generic_exec,
425         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
426         'exec_err' => $YES,
427         'fatal'    => $NO
428     },
429
430     {
431         'category' => 'preliminaries',
432         'subcategory' => 'server',
433         'detail'   => 'expected code version',
434         'err_msg'  => 'code version mis-match',
435         'function' => \&expected_code_version,
436         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
437             "$fwknopdCmd -c $cf{'def'} -a " .
438             "$cf{'def_access'} --version",
439         'fatal'    => $NO
440     },
441     {
442         'category' => 'preliminaries',
443         'detail'   => 'collecting system specifics',
444         'err_msg'  => 'could not get complete system specs',
445         'function' => \&specs,
446         'binary'   => $fwknopdCmd,
447         'fatal'    => $NO
448     },
449
450     {
451         'category' => 'basic operations',
452         'detail'   => 'dump config',
453         'err_msg'  => 'could not dump configuration',
454         'function' => \&generic_exec,
455         'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
456         'exec_err' => $NO,
457         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
458             "$fwknopdCmd -c $cf{'def'} " .
459             "-a $cf{'def_access'} --dump-config",
460         'fatal'    => $NO
461     },
462     {
463         'category' => 'basic operations',
464         'detail'   => 'override config',
465         'err_msg'  => 'could not override configuration',
466         'function' => \&generic_exec,
467         'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
468         'exec_err' => $NO,
469         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
470             "$fwknopdCmd $default_server_conf_args " .
471             "-O $conf_dir/override_fwknopd.conf --dump-config",
472         'fatal'    => $NO
473     },
474
475     {
476         'category' => 'basic operations',
477         'subcategory' => 'client',
478         'detail'   => '--get-key path validation',
479         'err_msg'  => 'accepted improper --get-key path',
480         'function' => \&generic_exec,
481         'positive_output_matches' => [qr/could\snot\sopen/i],
482         'exec_err' => $YES,
483         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
484             "$fwknopCmd -A tcp/22 -s $fake_ip " .
485             "-D $loopback_ip --get-key not/there",
486         'fatal'    => $YES
487     },
488     {
489         'category' => 'basic operations',
490         'subcategory' => 'client',
491         'detail'   => 'require [-s|-R|-a]',
492         'err_msg'  => 'allowed null allow IP',
493         'function' => \&generic_exec,
494         'positive_output_matches' => [qr/must\suse\sone\sof/i],
495         'exec_err' => $YES,
496         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
497             "$fwknopCmd -D $loopback_ip",
498         'fatal'    => $NO
499     },
500     {
501         'category' => 'basic operations',
502         'subcategory' => 'client',
503         'detail'   => '--allow-ip <IP> valid IP',
504         'err_msg'  => 'permitted invalid --allow-ip arg',
505         'function' => \&generic_exec,
506         'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
507         'exec_err' => $YES,
508         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
509             "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
510         'fatal'    => $NO
511     },
512     {
513         'category' => 'basic operations',
514         'subcategory' => 'client',
515         'detail'   => '-A <proto>/<port> specification (proto)',
516         'err_msg'  => 'permitted invalid -A <proto>/<port>',
517         'function' => \&generic_exec,
518         'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
519         'exec_err' => $YES,
520         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
521             "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
522         'fatal'    => $NO
523     },
524     {
525         'category' => 'basic operations',
526         'subcategory' => 'client',
527         'detail'   => '-A <proto>/<port> specification (port)',
528         'err_msg'  => 'permitted invalid -A <proto>/<port>',
529         'function' => \&generic_exec,
530         'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
531         'exec_err' => $YES,
532         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
533             "$fwknopCmd -A tcp/600001 -a $fake_ip -D $loopback_ip",
534         'fatal'    => $NO
535     },
536
537     {
538         'category' => 'basic operations',
539         'subcategory' => 'client',
540         'detail'   => 'generate SPA packet',
541         'err_msg'  => 'could not generate SPA packet',
542         'function' => \&client_send_spa_packet,
543         'cmdline'  => $default_client_args,
544         'fatal'    => $YES
545     },
546
547     {
548         'category' => 'basic operations',
549         'subcategory' => 'server',
550         'detail'   => 'list current fwknopd fw rules',
551         'err_msg'  => 'could not list current fwknopd fw rules',
552         'function' => \&generic_exec,
553         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
554             "$fwknopdCmd $default_server_conf_args --fw-list",
555         'fatal'    => $NO
556     },
557     {
558         'category' => 'basic operations',
559         'subcategory' => 'server',
560         'detail'   => 'list all current fw rules',
561         'err_msg'  => 'could not list all current fw rules',
562         'function' => \&generic_exec,
563         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
564             "$fwknopdCmd $default_server_conf_args --fw-list-all",
565         'fatal'    => $NO
566     },
567     {
568         'category' => 'basic operations',
569         'subcategory' => 'server',
570         'detail'   => 'flush current firewall rules',
571         'err_msg'  => 'could not flush current fw rules',
572         'function' => \&generic_exec,
573         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
574             "$fwknopdCmd $default_server_conf_args --fw-flush",
575         'fatal'    => $NO
576     },
577
578     {
579         'category' => 'basic operations',
580         'subcategory' => 'server',
581         'detail'   => 'start',
582         'err_msg'  => 'start error',
583         'function' => \&server_start,
584         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
585             "$fwknopdCmd $default_server_conf_args $intf_str",
586         'fatal'    => $NO
587     },
588     {
589         'category' => 'basic operations',
590         'subcategory' => 'server',
591         'detail'   => 'stop',
592         'err_msg'  => 'stop error',
593         'function' => \&server_stop,
594         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
595             "$fwknopdCmd $default_server_conf_args $intf_str",
596         'fatal'    => $NO
597     },
598     {
599         'category' => 'basic operations',
600         'subcategory' => 'server',
601         'detail'   => 'write PID',
602         'err_msg'  => 'did not write PID',
603         'function' => \&write_pid,
604         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
605             "$fwknopdCmd $default_server_conf_args $intf_str",
606         'fatal'    => $NO
607     },
608
609     {
610         'category' => 'basic operations',
611         'subcategory' => 'server',
612         'detail'   => '--packet-limit 1 exit',
613         'err_msg'  => 'did not exit after one packet',
614         'function' => \&server_packet_limit,
615         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
616             "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
617         'fatal'    => $NO
618     },
619     {
620         'category' => 'basic operations',
621         'subcategory' => 'server',
622         'detail'   => 'ignore packets < min SPA len (140)',
623         'err_msg'  => 'did not ignore small packets',
624         'function' => \&server_ignore_small_packets,
625         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
626             "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
627         'fatal'    => $NO
628     },
629     {
630         'category' => 'basic operations',
631         'subcategory' => 'server',
632         'detail'   => '-P bpf filter ignore packet',
633         'err_msg'  => 'filter did not ignore packet',
634         'function' => \&server_bpf_ignore_packet,
635         'cmdline'  => $default_client_args,
636         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
637             "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
638             qq|-P "udp port $non_std_spa_port"|,
639         'fatal'    => $NO
640     },
641
642     {
643         'category' => 'Rijndael SPA',
644         'subcategory' => 'client+server',
645         'detail'   => 'complete cycle (tcp/22 ssh)',
646         'err_msg'  => 'could not complete SPA cycle',
647         'function' => \&spa_cycle,
648         'cmdline'  => $default_client_args,
649         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
650             "$fwknopdCmd $default_server_conf_args $intf_str",
651         'fw_rule_created' => $NEW_RULE_REQUIRED,
652         'fw_rule_removed' => $NEW_RULE_REMOVED,
653         'fatal'    => $NO
654     },
655     {
656         'category' => 'Rijndael SPA',
657         'subcategory' => 'client+server',
658         'detail'   => 'permissions check cycle (tcp/22)',
659         'err_msg'  => 'could not complete SPA cycle',
660         'function' => \&permissions_check,
661         'cmdline'  => $default_client_args,
662         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
663             "$fwknopdCmd $default_server_conf_args $intf_str",
664         'server_positive_output_matches' => [qr/permissions\sshould\sonly\sbe\suser/],
665         'fw_rule_created' => $NEW_RULE_REQUIRED,
666         'fw_rule_removed' => $NEW_RULE_REMOVED,
667         'fatal'    => $NO
668     },
669     {
670         'category' => 'Rijndael SPA',
671         'subcategory' => 'client+server',
672         'detail'   => 'client IP resolve (tcp/22 ssh)',
673         'err_msg'  => 'could not complete SPA cycle',
674         'function' => \&spa_cycle,
675         'cmdline'  => $client_ip_resolve_args,
676         'no_ip_check' => 1,
677         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
678             "$fwknopdCmd $default_server_conf_args $intf_str",
679         'fw_rule_created' => $NEW_RULE_REQUIRED,
680         'fw_rule_removed' => $NEW_RULE_REMOVED,
681         'fatal'    => $NO
682     },
683
684     {
685         'category' => 'Rijndael SPA',
686         'subcategory' => 'client+server',
687         'detail'   => 'dual usage access key (tcp/80 http)',
688         'err_msg'  => 'could not complete SPA cycle',
689         'function' => \&spa_cycle,
690         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
691             "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
692             "$local_key_file --verbose --verbose",
693         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
694             "$fwknopdCmd -c $cf{'def'} -a $cf{'dual_key_access'} " .
695             "-d $default_digest_file -p $default_pid_file $intf_str",
696         ### check for the first stanza that does not allow tcp/80 - the
697         ### second stanza allows this
698         'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
699         'fw_rule_created' => $NEW_RULE_REQUIRED,
700         'fw_rule_removed' => $NEW_RULE_REMOVED,
701         'fatal'    => $NO
702     },
703     {
704         'category' => 'Rijndael SPA',
705         'subcategory' => 'client+server',
706         'detail'   => 'packet aging (past) (tcp/22 ssh)',
707         'err_msg'  => 'old SPA packet accepted',
708         'function' => \&spa_cycle,
709         'cmdline'  => "$default_client_args --time-offset-minus 300s",
710         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
711             "$fwknopdCmd $default_server_conf_args $intf_str",
712         'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
713         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
714         'fatal'    => $NO
715     },
716     {
717         'category' => 'Rijndael SPA',
718         'subcategory' => 'client+server',
719         'detail'   => 'packet aging (future) (tcp/22 ssh)',
720         'err_msg'  => 'future SPA packet accepted',
721         'function' => \&spa_cycle,
722         'cmdline'  => "$default_client_args --time-offset-plus 300s",
723         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
724             "$fwknopdCmd $default_server_conf_args $intf_str",
725         'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
726         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
727         'fatal'    => $NO
728     },
729     {
730         'category' => 'Rijndael SPA',
731         'subcategory' => 'client+server',
732         'detail'   => 'expired stanza (tcp/22 ssh)',
733         'err_msg'  => 'SPA packet accepted',
734         'function' => \&spa_cycle,
735         'cmdline'  => $default_client_args,
736         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
737             "$fwknopdCmd -c $cf{'def'} -a $cf{'exp_access'} " .
738             "-d $default_digest_file -p $default_pid_file $intf_str",
739         'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
740         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
741         'fatal'    => $NO
742     },
743     {
744         'category' => 'Rijndael SPA',
745         'subcategory' => 'client+server',
746         'detail'   => 'invalid expire date (tcp/22 ssh)',
747         'err_msg'  => 'SPA packet accepted',
748         'function' => \&spa_cycle,
749         'cmdline'  => $default_client_args,
750         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
751             "$fwknopdCmd -c $cf{'def'} -a $cf{'invalid_exp_access'} " .
752             "-d $default_digest_file -p $default_pid_file $intf_str",
753         'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
754         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
755         'fatal'    => $NO
756     },
757     {
758         'category' => 'Rijndael SPA',
759         'subcategory' => 'client+server',
760         'detail'   => 'expired epoch stanza (tcp/22 ssh)',
761         'err_msg'  => 'SPA packet accepted',
762         'function' => \&spa_cycle,
763         'cmdline'  => $default_client_args,
764         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
765             "$fwknopdCmd -c $cf{'def'} -a $cf{'exp_epoch_access'} " .
766             "-d $default_digest_file -p $default_pid_file $intf_str",
767         'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
768         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
769         'fatal'    => $NO
770     },
771     {
772         'category' => 'Rijndael SPA',
773         'subcategory' => 'client+server',
774         'detail'   => 'future expired stanza (tcp/22 ssh)',
775         'err_msg'  => 'SPA packet not accepted',
776         'function' => \&spa_cycle,
777         'cmdline'  => $default_client_args,
778         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
779             "$fwknopdCmd -c $cf{'def'} -a $cf{'future_exp_access'} " .
780             "-d $default_digest_file -p $default_pid_file $intf_str",
781         'fw_rule_created' => $NEW_RULE_REQUIRED,
782         'fw_rule_removed' => $NEW_RULE_REMOVED,
783         'fatal'    => $NO
784     },
785
786     {
787         'category' => 'Rijndael SPA',
788         'subcategory' => 'client+server',
789         'detail'   => 'OPEN_PORTS (tcp/22 ssh)',
790         'err_msg'  => "improper OPEN_PORTS result",
791         'function' => \&spa_cycle,
792         'cmdline'  => $default_client_args,
793         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
794             "$fwknopdCmd -c $cf{'def'} -a $cf{'open_ports_access'} " .
795             "-d $default_digest_file -p $default_pid_file $intf_str",
796         'fw_rule_created' => $NEW_RULE_REQUIRED,
797         'fw_rule_removed' => $NEW_RULE_REMOVED,
798         'fatal'    => $NO
799     },
800     {
801         'category' => 'Rijndael SPA',
802         'subcategory' => 'client+server',
803         'detail'   => 'OPEN_PORTS mismatch',
804         'err_msg'  => "SPA packet accepted",
805         'function' => \&spa_cycle,
806         'cmdline'  => $default_client_args,
807         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
808             "$fwknopdCmd -c $cf{'def'} -a $cf{'open_ports_mismatch'} " .
809             "-d $default_digest_file -p $default_pid_file $intf_str",
810         'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
811         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
812         'fatal'    => $NO
813     },
814     {
815         'category' => 'Rijndael SPA',
816         'subcategory' => 'client+server',
817         'detail'   => 'require user (tcp/22 ssh)',
818         'err_msg'  => "missed require user criteria",
819         'function' => \&spa_cycle,
820         'cmdline'  => "SPOOF_USER=$spoof_user $default_client_args",
821         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
822             "$fwknopdCmd -c $cf{'def'} -a $cf{'require_user_access'} " .
823             "-d $default_digest_file -p $default_pid_file $intf_str",
824         'fw_rule_created' => $NEW_RULE_REQUIRED,
825         'fw_rule_removed' => $NEW_RULE_REMOVED,
826         'fatal'    => $NO
827     },
828     {
829         'category' => 'Rijndael SPA',
830         'subcategory' => 'client+server',
831         'detail'   => 'user mismatch (tcp/22 ssh)',
832         'err_msg'  => "improper user accepted for access",
833         'function' => \&user_mismatch,
834         'function' => \&spa_cycle,
835         'cmdline'  => $default_client_args,
836         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
837             "$fwknopdCmd -c $cf{'def'} -a $cf{'user_mismatch_access'} " .
838             "-d $default_digest_file -p $default_pid_file $intf_str",
839         'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
840         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
841         'fatal'    => $NO
842     },
843     {
844         'category' => 'Rijndael SPA',
845         'subcategory' => 'client+server',
846         'detail'   => 'require src (tcp/22 ssh)',
847         'err_msg'  => "fw rule not created",
848         'function' => \&spa_cycle,
849         'cmdline'  => $default_client_args,
850         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
851             "$fwknopdCmd -c $cf{'def'} -a $cf{'require_src_access'} " .
852             "-d $default_digest_file -p $default_pid_file $intf_str",
853         'fw_rule_created' => $NEW_RULE_REQUIRED,
854         'fw_rule_removed' => $NEW_RULE_REMOVED,
855         'fatal'    => $NO
856     },
857     {
858         'category' => 'Rijndael SPA',
859         'subcategory' => 'client+server',
860         'detail'   => 'mismatch require src (tcp/22 ssh)',
861         'err_msg'  => "fw rule created",
862         'function' => \&spa_cycle,
863         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
864             "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
865             "$local_key_file --verbose --verbose",
866         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
867             "$fwknopdCmd -c $cf{'def'} -a $cf{'require_src_access'} " .
868             "-d $default_digest_file -p $default_pid_file $intf_str",
869         'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
870         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
871         'fatal'    => $NO
872     },
873
874     {
875         'category' => 'Rijndael SPA',
876         'subcategory' => 'client+server',
877         'detail'   => 'IP filtering (tcp/22 ssh)',
878         'err_msg'  => "did not filter $loopback_ip",
879         'function' => \&spa_cycle,
880         'cmdline'  => $default_client_args,
881         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
882             "$fwknopdCmd -c $cf{'def'} -a $cf{'no_src_match'} " .
883             "-d $default_digest_file -p $default_pid_file $intf_str",
884         'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
885         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
886         'fatal'    => $NO
887     },
888     {
889         'category' => 'Rijndael SPA',
890         'subcategory' => 'client+server',
891         'detail'   => 'subnet filtering (tcp/22 ssh)',
892         'err_msg'  => "did not filter $loopback_ip",
893         'function' => \&spa_cycle,
894         'cmdline'  => $default_client_args,
895         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
896             "$fwknopdCmd -c $cf{'def'} -a $cf{'no_subnet_match'} " .
897             "-d $default_digest_file -p $default_pid_file $intf_str",
898         'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
899         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
900         'fatal'    => $NO
901     },
902     {
903         'category' => 'Rijndael SPA',
904         'subcategory' => 'client+server',
905         'detail'   => 'IP+subnet filtering (tcp/22 ssh)',
906         'err_msg'  => "did not filter $loopback_ip",
907         'function' => \&spa_cycle,
908         'cmdline'  => $default_client_args,
909         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
910             "$fwknopdCmd -c $cf{'def'} -a $cf{'no_multi_src'} " .
911             "-d $default_digest_file -p $default_pid_file $intf_str",
912         'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
913         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
914         'fatal'    => $NO
915     },
916     {
917         'category' => 'Rijndael SPA',
918         'subcategory' => 'client+server',
919         'detail'   => 'IP match (tcp/22 ssh)',
920         'err_msg'  => "did not filter $loopback_ip",
921         'function' => \&spa_cycle,
922         'cmdline'  => $default_client_args,
923         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
924             "$fwknopdCmd -c $cf{'def'} -a $cf{'ip_src_match'} " .
925             "-d $default_digest_file -p $default_pid_file $intf_str",
926         'fw_rule_created' => $NEW_RULE_REQUIRED,
927         'fw_rule_removed' => $NEW_RULE_REMOVED,
928         'fatal'    => $NO
929     },
930     {
931         'category' => 'Rijndael SPA',
932         'subcategory' => 'client+server',
933         'detail'   => 'subnet match (tcp/22 ssh)',
934         'err_msg'  => "did not filter $loopback_ip",
935         'function' => \&spa_cycle,
936         'cmdline'  => $default_client_args,
937         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
938             "$fwknopdCmd -c $cf{'def'} -a $cf{'subnet_src_match'} " .
939             "-d $default_digest_file -p $default_pid_file $intf_str",
940         'fw_rule_created' => $NEW_RULE_REQUIRED,
941         'fw_rule_removed' => $NEW_RULE_REMOVED,
942         'fatal'    => $NO
943     },
944     {
945         'category' => 'Rijndael SPA',
946         'subcategory' => 'client+server',
947         'detail'   => 'multi IP/net match (tcp/22 ssh)',
948         'err_msg'  => "did not filter $loopback_ip",
949         'function' => \&spa_cycle,
950         'cmdline'  => $default_client_args,
951         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
952             "$fwknopdCmd -c $cf{'def'} -a $cf{'multi_src_access'} " .
953             "-d $default_digest_file -p $default_pid_file $intf_str",
954         'fw_rule_created' => $NEW_RULE_REQUIRED,
955         'fw_rule_removed' => $NEW_RULE_REMOVED,
956         'fatal'    => $NO
957     },
958     {
959         'category' => 'Rijndael SPA',
960         'subcategory' => 'client+server',
961         'detail'   => 'multi access stanzas (tcp/22 ssh)',
962         'err_msg'  => "could not complete SPA cycle",
963         'function' => \&spa_cycle,
964         'cmdline'  => $default_client_args,
965         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
966             "$fwknopdCmd -c $cf{'def'} -a $cf{'multi_stanza_access'} " .
967             "-d $default_digest_file -p $default_pid_file $intf_str",
968         'fw_rule_created' => $NEW_RULE_REQUIRED,
969         'fw_rule_removed' => $NEW_RULE_REMOVED,
970         'fatal'    => $NO
971     },
972     {
973         'category' => 'Rijndael SPA',
974         'subcategory' => 'client+server',
975         'detail'   => 'bad/good key stanzas (tcp/22 ssh)',
976         'err_msg'  => "could not complete SPA cycle",
977         'function' => \&spa_cycle,
978         'cmdline'  => $default_client_args,
979         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
980             "$fwknopdCmd -c $cf{'def'} -a $cf{'broken_keys_access'} " .
981             "-d $default_digest_file -p $default_pid_file $intf_str",
982         'fw_rule_created' => $NEW_RULE_REQUIRED,
983         'fw_rule_removed' => $NEW_RULE_REMOVED,
984         'fatal'    => $NO
985     },
986
987     {
988         'category' => 'Rijndael SPA',
989         'subcategory' => 'client+server',
990         'detail'   => "non-enabled NAT (tcp/22 ssh)",
991         'err_msg'  => "SPA packet not filtered",
992         'function' => \&spa_cycle,
993         'cmdline'  => "$default_client_args -N $internal_nat_host:22",
994         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
995             "$fwknopdCmd $default_server_conf_args $intf_str",
996         'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
997         'server_conf' => $cf{'nat'},
998         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
999         'fatal'    => $NO
1000     },
1001     {
1002         'category' => 'Rijndael SPA',
1003         'subcategory' => 'client+server',
1004         'detail'   => "NAT to $internal_nat_host (tcp/22 ssh)",
1005         'err_msg'  => "could not complete NAT SPA cycle",
1006         'function' => \&spa_cycle,
1007         'cmdline'  => "$default_client_args -N $internal_nat_host:22",
1008         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1009             "$fwknopdCmd -c $cf{'nat'} -a $cf{'open_ports_access'} " .
1010             "-d $default_digest_file -p $default_pid_file $intf_str",
1011         'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1012         'fw_rule_created' => $NEW_RULE_REQUIRED,
1013         'fw_rule_removed' => $NEW_RULE_REMOVED,
1014         'server_conf' => $cf{'nat'},
1015         'fatal'    => $NO
1016     },
1017     {
1018         'category' => 'Rijndael SPA',
1019         'subcategory' => 'client+server',
1020         'detail'   => "force NAT $force_nat_host (tcp/22 ssh)",
1021         'err_msg'  => "could not complete NAT SPA cycle",
1022         'function' => \&spa_cycle,
1023         'cmdline'  => $default_client_args,
1024         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1025             "$fwknopdCmd -c $cf{'nat'} -a $cf{'force_nat_access'} " .
1026             "-d $default_digest_file -p $default_pid_file $intf_str",
1027         'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
1028         'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1029         'fw_rule_created' => $NEW_RULE_REQUIRED,
1030         'fw_rule_removed' => $NEW_RULE_REMOVED,
1031         'server_conf' => $cf{'nat'},
1032         'fatal'    => $NO
1033     },
1034     {
1035         'category' => 'Rijndael SPA',
1036         'subcategory' => 'client+server',
1037         'detail'   => "local NAT $force_nat_host (tcp/22 ssh)",
1038         'err_msg'  => "could not complete NAT SPA cycle",
1039         'function' => \&spa_cycle,
1040         'cmdline'  => "$default_client_args --nat-local",
1041         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1042             "$fwknopdCmd -c $cf{'local_nat'} -a $cf{'force_nat_access'} " .
1043             "-d $default_digest_file -p $default_pid_file $intf_str",
1044         'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i,
1045             qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
1046         'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1047         'fw_rule_created' => $NEW_RULE_REQUIRED,
1048         'fw_rule_removed' => $NEW_RULE_REMOVED,
1049         'server_conf' => $cf{'nat'},
1050         'fatal'    => $NO
1051     },
1052     {
1053         'category' => 'Rijndael SPA',
1054         'subcategory' => 'client+server',
1055         'detail'   => "local NAT non-FORCE_NAT (tcp/22 ssh)",
1056         'err_msg'  => "could not complete NAT SPA cycle",
1057         'function' => \&spa_cycle,
1058         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1059             "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
1060             "$local_key_file --verbose --verbose --nat-local --nat-port 22",
1061         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1062             "$fwknopdCmd -c $cf{'local_nat'} -a $cf{'def_access'} " .
1063             "-d $default_digest_file -p $default_pid_file $intf_str",
1064         'server_positive_output_matches' => [qr/to\:$loopback_ip\:22/i,
1065             qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
1066         'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1067         'fw_rule_created' => $NEW_RULE_REQUIRED,
1068         'fw_rule_removed' => $NEW_RULE_REMOVED,
1069         'server_conf' => $cf{'nat'},
1070         'fatal'    => $NO
1071     },
1072
1073     {
1074         'category' => 'Rijndael SPA',
1075         'subcategory' => 'client+server',
1076         'detail'   => 'complete cycle (tcp/23 telnet)',
1077         'err_msg'  => 'could not complete SPA cycle',
1078         'function' => \&spa_cycle,
1079         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1080             "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1081             "$local_key_file --verbose --verbose",
1082         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1083             "$fwknopdCmd $default_server_conf_args $intf_str",
1084         'fw_rule_created' => $NEW_RULE_REQUIRED,
1085         'fw_rule_removed' => $NEW_RULE_REMOVED,
1086         'fatal'    => $NO
1087     },
1088     {
1089         'category' => 'Rijndael SPA',
1090         'subcategory' => 'client+server',
1091         'detail'   => 'complete cycle (tcp/9418 git)',
1092         'err_msg'  => 'could not complete SPA cycle',
1093         'function' => \&spa_cycle,
1094         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1095             "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1096             "$local_key_file --verbose --verbose",
1097         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1098             "$fwknopdCmd $default_server_conf_args $intf_str",
1099         'fw_rule_created' => $NEW_RULE_REQUIRED,
1100         'fw_rule_removed' => $NEW_RULE_REMOVED,
1101         'fatal'    => $NO
1102     },
1103     {
1104         'category' => 'Rijndael SPA',
1105         'subcategory' => 'client+server',
1106         'detail'   => 'complete cycle (tcp/60001)',
1107         'err_msg'  => 'could not complete SPA cycle',
1108         'function' => \&spa_cycle,
1109         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1110             "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
1111             "$local_key_file --verbose --verbose",
1112         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1113             "$fwknopdCmd $default_server_conf_args $intf_str",
1114         'fw_rule_created' => $NEW_RULE_REQUIRED,
1115         'fw_rule_removed' => $NEW_RULE_REMOVED,
1116         'fatal'    => $NO
1117     },
1118     {
1119         'category' => 'Rijndael SPA',
1120         'subcategory' => 'client+server',
1121         'detail'   => 'multi port (tcp/60001,udp/60001)',
1122         'err_msg'  => 'could not complete SPA cycle',
1123         'function' => \&spa_cycle,
1124         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1125             "$fwknopCmd -A tcp/60001,udp/60001 -a $fake_ip -D $loopback_ip --get-key " .
1126             "$local_key_file --verbose --verbose",
1127         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1128             "$fwknopdCmd $default_server_conf_args $intf_str",
1129         'fw_rule_created' => $NEW_RULE_REQUIRED,
1130         'fw_rule_removed' => $NEW_RULE_REMOVED,
1131         'fatal'    => $NO
1132     },
1133     {
1134         'category' => 'Rijndael SPA',
1135         'subcategory' => 'client+server',
1136         'detail'   => 'multi port (tcp/22,udp/53,tcp/1234)',
1137         'err_msg'  => 'could not complete SPA cycle',
1138         'function' => \&spa_cycle,
1139         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1140             "$fwknopCmd -A tcp/22,udp/53,tcp/1234 -a $fake_ip -D $loopback_ip --get-key " .
1141             "$local_key_file --verbose --verbose",
1142         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1143             "$fwknopdCmd $default_server_conf_args $intf_str",
1144         'fw_rule_created' => $NEW_RULE_REQUIRED,
1145         'fw_rule_removed' => $NEW_RULE_REMOVED,
1146         'fatal'    => $NO
1147     },
1148
1149     {
1150         'category' => 'Rijndael SPA',
1151         'subcategory' => 'client+server',
1152         'detail'   => 'complete cycle (udp/53 dns)',
1153         'err_msg'  => 'could not complete SPA cycle',
1154         'function' => \&spa_cycle,
1155         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1156             "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1157             "$local_key_file --verbose --verbose",
1158         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1159             "$fwknopdCmd $default_server_conf_args $intf_str",
1160         'fw_rule_created' => $NEW_RULE_REQUIRED,
1161         'fw_rule_removed' => $NEW_RULE_REMOVED,
1162         'fatal'    => $NO
1163     },
1164     {
1165         'category' => 'Rijndael SPA',
1166         'subcategory' => 'client+server',
1167         'detail'   => "-P bpf SPA over port $non_std_spa_port",
1168         'err_msg'  => 'could not complete SPA cycle',
1169         'function' => \&spa_cycle,
1170         'cmdline'  => "$default_client_args --server-port $non_std_spa_port",
1171         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1172             "$fwknopdCmd $default_server_conf_args $intf_str " .
1173             qq|-P "udp port $non_std_spa_port"|,
1174         'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1175         'fw_rule_created' => $NEW_RULE_REQUIRED,
1176         'fw_rule_removed' => $NEW_RULE_REMOVED,
1177         'fatal'    => $NO
1178     },
1179
1180     {
1181         'category' => 'Rijndael SPA',
1182         'subcategory' => 'client+server',
1183         'detail'   => 'random SPA port (tcp/22 ssh)',
1184         'err_msg'  => 'could not complete SPA cycle',
1185         'function' => \&spa_cycle,
1186         'cmdline'  => "$default_client_args -r",
1187         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1188             "$fwknopdCmd $default_server_conf_args $intf_str " .
1189             qq|-P "udp"|,
1190         'fw_rule_created' => $NEW_RULE_REQUIRED,
1191         'fw_rule_removed' => $NEW_RULE_REMOVED,
1192         'fatal'    => $NO
1193     },
1194
1195     {
1196         'category' => 'Rijndael SPA',
1197         'subcategory' => 'client+server',
1198         'detail'   => 'spoof username (tcp/22)',
1199         'err_msg'  => 'could not spoof username',
1200         'function' => \&spoof_username,
1201         'cmdline'  => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1202             "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1203             "$local_key_file --verbose --verbose",
1204         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1205             "$fwknopdCmd $default_server_conf_args $intf_str",
1206         'fatal'    => $NO
1207     },
1208
1209     {
1210         'category' => 'Rijndael SPA',
1211         'subcategory' => 'client+server',
1212         'detail'   => 'replay attack detection',
1213         'err_msg'  => 'could not detect replay attack',
1214         'function' => \&replay_detection,
1215         'cmdline'  => $default_client_args,
1216         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1217             "$fwknopdCmd $default_server_conf_args $intf_str",
1218         'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1219         'fatal'    => $NO
1220     },
1221     {
1222         'category' => 'Rijndael SPA',
1223         'subcategory' => 'client+server',
1224         'detail'   => 'replay detection (Rijndael prefix)',
1225         'err_msg'  => 'could not detect replay attack',
1226         'function' => \&replay_detection,
1227         'pkt_prefix' => 'U2FsdGVkX1',
1228         'cmdline'  => $default_client_args,
1229         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1230             "$fwknopdCmd $default_server_conf_args $intf_str",
1231         'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1232         'fatal'    => $NO
1233     },
1234
1235     ### fuzzing tests
1236     {
1237         'category' => 'Rijndael SPA',
1238         'subcategory' => 'FUZZING',
1239         'detail'   => 'overly long port value',
1240         'err_msg'  => 'server crashed or did not detect error condition',
1241         'function' => \&fuzzer,
1242         ### this packet was generated with a modified fwknop client via the
1243         ### following command line:
1244         #
1245         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
1246         # "tcp/`perl -e '{print "1"x"40"}'`" -a 127.0.0.2 -D 127.0.0.1 \
1247         # --get-key local_spa.key --verbose --verbose
1248         #
1249         # This problem was found by Fernando Arnaboldi of IOActive and exploits
1250         # a buffer overflow in the fwknopd servers prior to 2.0.3 from
1251         # authenticated clients.
1252         #
1253         'fuzzing_pkt' =>
1254             '+JzxeTGlc6lwwzbJSrYChKx8bonWBIPajwGfEtGOaoglcMLbTY/GGXo/nxqiN1LykFS' .
1255             'lDFXgrkyx2emJ7NGzYqQPUYZxLdZRocR9aRIptvXLLIPBcIpJASi/TUiJlw7CDFMcj0' .
1256             'ptSBJJUZi0tozpKHETp3AgqfzyOy5FNs38aZsV5/sDl3Pt+kF7fTZJ+YLbmYY4yCUz2' .
1257             'ZUYoCaJ7X78ULyJTi5eT7nug',
1258         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1259         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1260             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1261             "-d $default_digest_file -p $default_pid_file $intf_str",
1262         'fatal'    => $NO
1263     },
1264     {
1265         'category' => 'Rijndael SPA',
1266         'subcategory' => 'FUZZING',
1267         'detail'   => 'overly long proto value',
1268         'err_msg'  => 'server crashed or did not detect error condition',
1269         'function' => \&fuzzer,
1270         ### this packet was generated with a modified fwknop client via the
1271         ### following command line:
1272         #
1273         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
1274         # "tcp`perl -e '{print "A"x"28"}'`/1" -a 127.0.0.2 -D 127.0.0.1 \
1275         # --get-key local_spa.key --verbose --verbose
1276         #
1277         # This problem was found by Fernando Arnaboldi of IOActive and exploits
1278         # a buffer overflow in the fwknopd servers prior to 2.0.3 from
1279         # authenticated clients.
1280         #
1281         'fuzzing_pkt' =>
1282             '/im5MiJQmOdzqrdWXv+AjEtAm/HsLrdaTFcSw3ZskqpGOdDIrSCz3VXbFfv7qDkc5Y4' .
1283             'q/k1mRXl9SGzpug87U5dZSyCdAr30z7/2kUFEPTGOQBi/x+L1t1pvdkm4xg13t09ldm' .
1284             '5OD8KiV6qzqLOvN4ULJjvvJJWBZ9qvo/f2Q9Wf67g2KHiwS6EeCINAuMoUw/mNRQMa4' .
1285             'oGnOXu3/DeWHJAwtSeh7EAr4',
1286         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1287         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1288             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1289             "-d $default_digest_file -p $default_pid_file $intf_str",
1290         'fatal'    => $NO
1291     },
1292     {
1293         'category' => 'Rijndael SPA',
1294         'subcategory' => 'FUZZING',
1295         'detail'   => 'overly long IP value',
1296         'err_msg'  => 'server crashed or did not detect error condition',
1297         'function' => \&fuzzer,
1298         ### this packet was generated with a modified fwknop client via the
1299         ### following command line:
1300         #
1301         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
1302         # -a `perl -e '{print "1"x"136"}'`.0.0.1 -D 127.0.0.1 \
1303         # --get-key local_spa.key --verbose --verbose
1304         #
1305         # This problem was found by Fernando Arnaboldi of IOActive and exploits
1306         # a condition in which pre-2.0.3 fwknopd servers fail to properly validate
1307         # allow IP addresses from malicious authenticated clients.
1308         #
1309         'fuzzing_pkt' =>
1310             '93f2rhsXLmBoPicWvYTqrbp+6lNqvWDc8dzmX2s3settwjBGRAXm33TB9agibEphrBu' .
1311             '3d+7DEsivZLDS6Kz0JwdjX7t0J9c8es+DVNjlLnPtVNcxhs+2kUzimNrgysIXQRJ+GF' .
1312             'GbhdxiXCqdy1vWxWpdoaZmY/CeGIkpoFJFPbJhCRLLX25UMvMF2wXj02MpI4d3t1/6W' .
1313             'DM3taM3kZsiFv6HxFjAhIEuQ1oAg2OgRGXkDmT3jDNZMHUm0d4Ahm9LonG7RbOxq/B0' .
1314             'qUvY8lkymbwvjelVok7Lvlc06cRhN4zm32D4V05g0vQS3PlX9C+mgph9DeAPVX+D8iZ' .
1315             '8lGrxcPSfbCOW61k0MP+q1EhLZkc1qAm5g2+2cLNZcoBNEdh3yj8OTPZJyBVw',
1316         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1317         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1318             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1319             "-d $default_digest_file -p $default_pid_file $intf_str",
1320         'fatal'    => $NO
1321     },
1322     {
1323         'category' => 'Rijndael SPA',
1324         'subcategory' => 'FUZZING',
1325         'detail'   => 'negative port value',
1326         'err_msg'  => 'server crashed or did not detect error condition',
1327         'function' => \&fuzzer,
1328         ### this packet was generated with a modified fwknop client via the
1329         ### following command line:
1330         #
1331         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
1332         # tcp/-33 -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
1333         # --verbose --verbose
1334         #
1335         'fuzzing_pkt' =>
1336             '/weoc+pEuQknZo8ImWTQBB+/PwSJ2/TcrmFoSkxpRXX4+jlUxoJakHrioxh8rhLmAD9' .
1337             '8E4lMnq+EbM2XYdhs2alpZ5bovAFojMsYRWwr/BvRO4Um4Fmo9z9sY3DR477TXNYXBR' .
1338             'iGXWxSL4u+AWSSePK3qiiYoRQVw',
1339         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1340         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1341             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1342             "-d $default_digest_file -p $default_pid_file $intf_str",
1343         'fatal'    => $NO
1344     },
1345     {
1346         'category' => 'Rijndael SPA',
1347         'subcategory' => 'FUZZING',
1348         'detail'   => 'null port value',
1349         'err_msg'  => 'server crashed or did not detect error condition',
1350         'function' => \&fuzzer,
1351         ### this packet was generated with a modified fwknop client via the
1352         ### following command line:
1353         #
1354         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/ \
1355         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
1356         # --verbose --verbose
1357         #
1358         'fuzzing_pkt' =>
1359             '94nu7hvq6V/3A27GzjHwfPnPCQfs44ySlraIFYHOAqy5YqjkrBS67nH35tX55N1BrYZ' .
1360             '07zvcT03keUhLE1Uo7Wme1nE7BfTOG5stmIK1UQI85sL52//lDHu+xCqNcL7GUKbVRz' .
1361             'ekw+EUscVvUkrsRcVtSvOm+fCNo',
1362         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1363         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1364             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1365             "-d $default_digest_file -p $default_pid_file $intf_str",
1366         'fatal'    => $NO
1367     },
1368     {
1369         'category' => 'Rijndael SPA',
1370         'subcategory' => 'FUZZING',
1371         'detail'   => 'long FKO protocol value (enc mode trigger)',
1372         'err_msg'  => 'server crashed or did not detect error condition',
1373         'function' => \&fuzzer,
1374         ### this packet was generated with a modified fwknop client via the
1375         ### following command line:
1376         #
1377         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
1378         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
1379         #
1380         # This problem was found by Fernando Arnaboldi of IOActive and is designed
1381         # to have fwknopd look for a mode decryption mode for a long Rijndael-
1382         # encrypted SPA packet
1383         #
1384         'fuzzing_pkt' =>
1385             '/ewH/k1XsDX+VQ8NlNvCZ4P2QOl/4IpJYXkq4TtAe3899OtApXJiTtPCuYW70XPuxge' .
1386             'MtFjc4UfslK/r9v+FYfyd3fIIHCz0Q0M4+nM3agTLmJj8nOxk6ZeBj82SDQWhHAxGdJ' .
1387             'IQALPve0ug4cuGxS3b4M+2Q/Av9i2tU3Lzlogw3sY0tk6wGf4zZk4UsviVXYpINniGT' .
1388             'RhYSIQ1dfdkng7hKiHMDaObYY1GFp4nxEt/QjasAwvE+7/iFyoKN+IRpGG4v4hGEPh2' .
1389             'vTDqmvfRuIHtgFD7NxZjt+m/jjcu0gkdWEoD4fenwGU35FlvchyM2AiAEw7yRzSABfn' .
1390             'R9d3sYZGMtyASw2O1vSluwIxUUnDop3gxEIhJEj8h+01pA3K+klSpALeY9EZgHqYC7E' .
1391             'ETuPS6dZ3764nWohtCY67JvNUX7TtNDNc2qrhrapdRP17+PT2Vh4s9m38V3WwVWC3uH' .
1392             'X/klLZcHIt+aRDV+uekw9GOKSgwFL2ekPpr3gXxigc3zrxel5hcsqLOpVUa4CP/0HkG' .
1393             'F0NPQvOT3ZvpeIJnirKP1ZX9gDFinqhuzL7oqktW61e1iwe7KZEdrZV0k2KZwyb8qU5' .
1394             'rPAEnw',
1395         'server_positive_output_matches' => [qr/No\sstanza\sencryption\smode\smatch/],
1396         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1397             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1398             "-d $default_digest_file -p $default_pid_file $intf_str",
1399         'fatal'    => $NO
1400     },
1401     {
1402         'category' => 'Rijndael SPA',
1403         'subcategory' => 'FUZZING',
1404         'detail'   => 'long FKO protocol value (Rijndael trigger)',
1405         'err_msg'  => 'server crashed or did not detect error condition',
1406         'function' => \&fuzzer,
1407         ### this packet was generated with a modified fwknop client via the
1408         ### following command line:
1409         #
1410         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
1411         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
1412         #
1413         # This problem was found by Fernando Arnaboldi of IOActive and is designed
1414         # to have fwknopd look for a mode decryption mode for a long Rijndael-
1415         # encrypted SPA packet
1416         #
1417         'fuzzing_pkt' =>
1418             '+YQNu4BFgiNeu8HeiBiNKriqCFSseALt9vJaKzkzK/OF4pjkJcvhGEOi7fEVXqn3VIdlGR' .
1419             'DmBul2I7H3z18U9E97bWGgT9NexKgEPCuekL18ZEPf5xR3JleNsNWatqYgAOkgN8ZWE69Q' .
1420             'qQUYYhxTvJHS6R+5JqFKB3A44hMXoICdYNkn9MAktHxk3PbbpQ+nA+jESwVCra2doAiLiM' .
1421             'ucvGIZZiTv0Mc1blFYIE2zqZ/C7ct1V+ukwSkUv0r87eA7uJhmlpThRsL0dN6iekJ6i87B' .
1422             'tE8QyuOXzOMftI11SUn/LwqD4RMdR21rvLrzR6ZB5eUX2UBpODyzX6n+PJJkTWCuFVT4z1' .
1423             'MKY',
1424         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1425         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1426             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1427             "-d $default_digest_file -p $default_pid_file $intf_str",
1428         'fatal'    => $NO
1429     },
1430
1431     {
1432         'category' => 'Rijndael SPA',
1433         'subcategory' => 'FUZZING',
1434         'detail'   => 'null proto value',
1435         'err_msg'  => 'server crashed or did not detect error condition',
1436         'function' => \&fuzzer,
1437         ### this packet was generated with a modified fwknop client via the
1438         ### following command line:
1439         #
1440         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A /22 \
1441         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
1442         # --verbose --verbose
1443         #
1444         'fuzzing_pkt' =>
1445             '/JT14qxh9P4iy+CuUZahThaQjoEuL2zd46a+jL6sTrBZJSa6faUX4dH5fte/4ZJv+9f' .
1446             'd/diWYKAUvdQ4DydPGlR7mwQa2W+obKpqrsTBz7D4054z6ATAOGpCtifakEVl1XRc2+' .
1447             'hW04WpY8mdUNu9i+PrfPr7/KxqU',
1448         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
1449         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1450             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
1451             "-d $default_digest_file -p $default_pid_file $intf_str",
1452         'fatal'    => $NO
1453     },
1454     {
1455         'category' => 'FUZZING',
1456         'subcategory' => 'server',
1457         'detail'   => 'invalid SOURCE access.conf',
1458         'err_msg'  => 'server crashed or did not detect error condition',
1459         'function' => \&generic_exec,
1460         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1461             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_source'} " .
1462             "-d $default_digest_file -p $default_pid_file $intf_str",
1463         'positive_output_matches' => [qr/Fatal\sinvalid/],
1464         'exec_err' => $YES,
1465         'fatal'    => $NO
1466     },
1467     {
1468         'category' => 'FUZZING',
1469         'subcategory' => 'server',
1470         'detail'   => 'invalid OPEN_PORTS access.conf',
1471         'err_msg'  => 'server crashed or did not detect error condition',
1472         'function' => \&generic_exec,
1473         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1474             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_open_ports'} " .
1475             "-d $default_digest_file -p $default_pid_file $intf_str",
1476         'positive_output_matches' => [qr/Fatal\sinvalid/],
1477         'exec_err' => $YES,
1478         'fatal'    => $NO
1479     },
1480     {
1481         'category' => 'FUZZING',
1482         'subcategory' => 'server',
1483         'detail'   => 'invalid RESTRICT_PORTS access.conf',
1484         'err_msg'  => 'server crashed or did not detect error condition',
1485         'function' => \&generic_exec,
1486         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1487             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'fuzz_restrict_ports'} " .
1488             "-d $default_digest_file -p $default_pid_file $intf_str",
1489         'positive_output_matches' => [qr/Fatal\sinvalid/],
1490         'exec_err' => $YES,
1491         'fatal'    => $NO
1492     },
1493
1494     {
1495         'category' => 'Rijndael SPA',
1496         'subcategory' => 'server',
1497         'detail'   => 'digest cache structure',
1498         'err_msg'  => 'improper digest cache structure',
1499         'function' => \&digest_cache_structure,
1500         'fatal'    => $NO
1501     },
1502
1503     {
1504         'category' => 'Rijndael SPA',
1505         'subcategory' => 'server',
1506         'detail'   => 'ipfw active/expire sets not equal',
1507         'err_msg'  => 'allowed active/expire sets to be the same',
1508         'function' => \&spa_cycle,
1509         'cmdline'  => $default_client_args,
1510         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1511             "$fwknopdCmd -c $cf{'ipfw_active_expire'} -a $cf{'def_access'} " .
1512             "-d $default_digest_file -p $default_pid_file $intf_str",
1513         'server_positive_output_matches' => [qr/Cannot\sset\sidentical\sipfw\sactive\sand\sexpire\ssets/],
1514         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
1515         'fatal'    => $NO
1516     },
1517
1518     {
1519         'category' => 'Rijndael SPA',
1520         'subcategory' => 'client+server',
1521         'detail'   => 'non-base64 altered SPA data',
1522         'err_msg'  => 'allowed improper SPA data',
1523         'function' => \&altered_non_base64_spa_data,
1524         'cmdline'  => $default_client_args,
1525         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1526             "$fwknopdCmd $default_server_conf_args $intf_str",
1527         'fatal'    => $NO
1528     },
1529     {
1530         'category' => 'Rijndael SPA',
1531         'subcategory' => 'client+server',
1532         'detail'   => 'base64 altered SPA data',
1533         'err_msg'  => 'allowed improper SPA data',
1534         'function' => \&altered_base64_spa_data,
1535         'cmdline'  => $default_client_args,
1536         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1537             "$fwknopdCmd $default_server_conf_args $intf_str",
1538         'fatal'    => $NO
1539     },
1540     {
1541         'category' => 'Rijndael SPA',
1542         'subcategory' => 'client+server',
1543         'detail'   => 'appended data to SPA pkt',
1544         'err_msg'  => 'allowed improper SPA data',
1545         'function' => \&appended_spa_data,
1546         'cmdline'  => $default_client_args,
1547         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1548             "$fwknopdCmd $default_server_conf_args $intf_str",
1549         'fatal'    => $NO
1550     },
1551     {
1552         'category' => 'Rijndael SPA',
1553         'subcategory' => 'client+server',
1554         'detail'   => 'prepended data to SPA pkt',
1555         'err_msg'  => 'allowed improper SPA data',
1556         'function' => \&prepended_spa_data,
1557         'cmdline'  => $default_client_args,
1558         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1559             "$fwknopdCmd $default_server_conf_args $intf_str",
1560         'fatal'    => $NO
1561     },
1562
1563     {
1564         'category' => 'GPG (no pw) SPA',
1565         'subcategory' => 'client+server',
1566         'detail'   => 'complete cycle (tcp/22 ssh)',
1567         'err_msg'  => 'could not complete SPA cycle',
1568         'function' => \&spa_cycle,
1569         'cmdline'  => "$default_client_gpg_args_no_homedir "
1570             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1571         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1572         'fw_rule_created' => $NEW_RULE_REQUIRED,
1573         'fw_rule_removed' => $NEW_RULE_REMOVED,
1574         'fatal'    => $NO
1575     },
1576     {
1577         'category' => 'GPG (no pw) SPA',
1578         'subcategory' => 'client+server',
1579         'detail'   => 'multi gpg-IDs (tcp/22 ssh)',
1580         'err_msg'  => 'could not complete SPA cycle',
1581         'function' => \&spa_cycle,
1582         'cmdline'  => "$default_client_gpg_args_no_homedir "
1583             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1584         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir " .
1585             "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
1586             "-a $cf{'multi_gpg_access'} $intf_str " .
1587             "-d $default_digest_file -p $default_pid_file",
1588         'fw_rule_created' => $NEW_RULE_REQUIRED,
1589         'fw_rule_removed' => $NEW_RULE_REMOVED,
1590         'fatal'    => $NO
1591     },
1592
1593     {
1594         'category' => 'GPG (no pw) SPA',
1595         'subcategory' => 'client+server',
1596         'detail'   => 'complete cycle (tcp/23 telnet)',
1597         'err_msg'  => 'could not complete SPA cycle',
1598         'function' => \&spa_cycle,
1599         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1600             "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1601             "$local_key_file --verbose --verbose " .
1602             "--gpg-recipient-key $gpg_server_key " .
1603             "--gpg-signer-key $gpg_client_key " .
1604             "--gpg-home-dir $gpg_client_home_dir_no_pw",
1605         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1606         'fw_rule_created' => $NEW_RULE_REQUIRED,
1607         'fw_rule_removed' => $NEW_RULE_REMOVED,
1608         'fatal'    => $NO
1609     },
1610     {
1611         'category' => 'GPG (no pw) SPA',
1612         'subcategory' => 'client+server',
1613         'detail'   => 'complete cycle (tcp/9418 git)',
1614         'err_msg'  => 'could not complete SPA cycle',
1615         'function' => \&spa_cycle,
1616         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1617             "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1618             "$local_key_file --verbose --verbose " .
1619             "--gpg-recipient-key $gpg_server_key " .
1620             "--gpg-signer-key $gpg_client_key " .
1621             "--gpg-home-dir $gpg_client_home_dir_no_pw",
1622         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1623         'fw_rule_created' => $NEW_RULE_REQUIRED,
1624         'fw_rule_removed' => $NEW_RULE_REMOVED,
1625         'fatal'    => $NO
1626     },
1627     {
1628         'category' => 'GPG (no pw) SPA',
1629         'subcategory' => 'client+server',
1630         'detail'   => 'complete cycle (tcp/60001)',
1631         'err_msg'  => 'could not complete SPA cycle',
1632         'function' => \&spa_cycle,
1633         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1634             "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
1635             "$local_key_file --verbose --verbose " .
1636             "--gpg-recipient-key $gpg_server_key " .
1637             "--gpg-signer-key $gpg_client_key " .
1638             "--gpg-home-dir $gpg_client_home_dir_no_pw",
1639         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1640         'fw_rule_created' => $NEW_RULE_REQUIRED,
1641         'fw_rule_removed' => $NEW_RULE_REMOVED,
1642         'fatal'    => $NO
1643     },
1644
1645     {
1646         'category' => 'GPG (no pw) SPA',
1647         'subcategory' => 'client+server',
1648         'detail'   => 'complete cycle (udp/53 dns)',
1649         'err_msg'  => 'could not complete SPA cycle',
1650         'function' => \&spa_cycle,
1651         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1652             "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1653             "$local_key_file --verbose --verbose " .
1654             "--gpg-recipient-key $gpg_server_key " .
1655             "--gpg-signer-key $gpg_client_key " .
1656             "--gpg-home-dir $gpg_client_home_dir_no_pw",
1657         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1658         'fw_rule_created' => $NEW_RULE_REQUIRED,
1659         'fw_rule_removed' => $NEW_RULE_REMOVED,
1660         'fatal'    => $NO
1661     },
1662
1663     {
1664         'category' => 'GPG (no pw) SPA',
1665         'subcategory' => 'client+server',
1666         'detail'   => 'replay attack detection',
1667         'err_msg'  => 'could not detect replay attack',
1668         'function' => \&replay_detection,
1669         'cmdline'  => "$default_client_gpg_args_no_homedir "
1670             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1671         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1672         'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1673         'fatal'    => $NO
1674     },
1675     {
1676         'category' => 'GPG (no pw) SPA',
1677         'subcategory' => 'client+server',
1678         'detail'   => 'replay detection (GnuPG prefix)',
1679         'err_msg'  => 'could not detect replay attack',
1680         'function' => \&replay_detection,
1681         'pkt_prefix' => 'hQ',
1682         'cmdline'  => "$default_client_gpg_args_no_homedir "
1683             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1684         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1685             "$fwknopdCmd $default_server_conf_args $intf_str",
1686         'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1687         'fatal'    => $NO
1688     },
1689
1690     {
1691         'category' => 'GPG (no pw) SPA',
1692         'subcategory' => 'client+server',
1693         'detail'   => 'non-base64 altered SPA data',
1694         'err_msg'  => 'allowed improper SPA data',
1695         'function' => \&altered_non_base64_spa_data,
1696         'cmdline'  => "$default_client_gpg_args_no_homedir "
1697             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1698         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1699         'fatal'    => $NO
1700     },
1701     {
1702         'category' => 'GPG (no pw) SPA',
1703         'subcategory' => 'client+server',
1704         'detail'   => 'base64 altered SPA data',
1705         'err_msg'  => 'allowed improper SPA data',
1706         'function' => \&altered_base64_spa_data,
1707         'cmdline'  => "$default_client_gpg_args_no_homedir "
1708             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1709         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1710         'fatal'    => $NO
1711     },
1712     {
1713         'category' => 'GPG (no pw) SPA',
1714         'subcategory' => 'client+server',
1715         'detail'   => 'appended data to SPA pkt',
1716         'err_msg'  => 'allowed improper SPA data',
1717         'function' => \&appended_spa_data,
1718         'cmdline'  => "$default_client_gpg_args_no_homedir "
1719             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1720         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1721         'fatal'    => $NO
1722     },
1723     {
1724         'category' => 'GPG (no pw) SPA',
1725         'subcategory' => 'client+server',
1726         'detail'   => 'prepended data to SPA pkt',
1727         'err_msg'  => 'allowed improper SPA data',
1728         'function' => \&prepended_spa_data,
1729         'cmdline'  => "$default_client_gpg_args_no_homedir "
1730             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1731         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1732         'fatal'    => $NO
1733     },
1734     {
1735         'category' => 'GPG (no pw) SPA',
1736         'subcategory' => 'client+server',
1737         'detail'   => 'spoof username (tcp/22 ssh)',
1738         'err_msg'  => 'could not spoof username',
1739         'function' => \&spoof_username,
1740         'cmdline'  => "SPOOF_USER=$spoof_user $default_client_gpg_args_no_homedir "
1741             . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1742         'fwknopd_cmdline'  => $default_server_gpg_args_no_pw,
1743         'fatal'    => $NO
1744     },
1745
1746     {
1747         'category' => 'GnuPG (GPG) SPA',
1748         'subcategory' => 'client+server',
1749         'detail'   => 'complete cycle (tcp/22 ssh)',
1750         'err_msg'  => 'could not complete SPA cycle',
1751         'function' => \&spa_cycle,
1752         'cmdline'  => $default_client_gpg_args,
1753         'fwknopd_cmdline'  => $default_server_gpg_args,
1754         'fw_rule_created' => $NEW_RULE_REQUIRED,
1755         'fw_rule_removed' => $NEW_RULE_REMOVED,
1756         'fatal'    => $NO
1757     },
1758     {
1759         'category' => 'GnuPG (GPG) SPA',
1760         'subcategory' => 'client+server',
1761         'detail'   => 'multi gpg-IDs (tcp/22 ssh)',
1762         'err_msg'  => 'could not complete SPA cycle',
1763         'function' => \&spa_cycle,
1764         'cmdline'  => $default_client_gpg_args,
1765         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir " .
1766             "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
1767             "-a $cf{'multi_gpg_access'} $intf_str " .
1768             "-d $default_digest_file -p $default_pid_file",
1769         'fw_rule_created' => $NEW_RULE_REQUIRED,
1770         'fw_rule_removed' => $NEW_RULE_REMOVED,
1771         'fatal'    => $NO
1772     },
1773
1774     {
1775         'category' => 'GnuPG (GPG) SPA',
1776         'subcategory' => 'client+server',
1777         'detail'   => 'complete cycle (tcp/23 telnet)',
1778         'err_msg'  => 'could not complete SPA cycle',
1779         'function' => \&spa_cycle,
1780         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1781             "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1782             "$local_key_file --verbose --verbose " .
1783             "--gpg-recipient-key $gpg_server_key " .
1784             "--gpg-signer-key $gpg_client_key " .
1785             "--gpg-home-dir $gpg_client_home_dir",
1786         'fwknopd_cmdline'  => $default_server_gpg_args,
1787         'fw_rule_created' => $NEW_RULE_REQUIRED,
1788         'fw_rule_removed' => $NEW_RULE_REMOVED,
1789         'fatal'    => $NO
1790     },
1791     {
1792         'category' => 'GnuPG (GPG) SPA',
1793         'subcategory' => 'client+server',
1794         'detail'   => 'complete cycle (tcp/9418 git)',
1795         'err_msg'  => 'could not complete SPA cycle',
1796         'function' => \&spa_cycle,
1797         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1798             "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1799             "$local_key_file --verbose --verbose " .
1800             "--gpg-recipient-key $gpg_server_key " .
1801             "--gpg-signer-key $gpg_client_key " .
1802             "--gpg-home-dir $gpg_client_home_dir",
1803         'fwknopd_cmdline'  => $default_server_gpg_args,
1804         'fw_rule_created' => $NEW_RULE_REQUIRED,
1805         'fw_rule_removed' => $NEW_RULE_REMOVED,
1806         'fatal'    => $NO
1807     },
1808     {
1809         'category' => 'GnuPG (GPG) SPA',
1810         'subcategory' => 'client+server',
1811         'detail'   => 'complete cycle (tcp/60001)',
1812         'err_msg'  => 'could not complete SPA cycle',
1813         'function' => \&spa_cycle,
1814         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1815             "$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
1816             "$local_key_file --verbose --verbose " .
1817             "--gpg-recipient-key $gpg_server_key " .
1818             "--gpg-signer-key $gpg_client_key " .
1819             "--gpg-home-dir $gpg_client_home_dir",
1820         'fwknopd_cmdline'  => $default_server_gpg_args,
1821         'fw_rule_created' => $NEW_RULE_REQUIRED,
1822         'fw_rule_removed' => $NEW_RULE_REMOVED,
1823         'fatal'    => $NO
1824     },
1825
1826     {
1827         'category' => 'GnuPG (GPG) SPA',
1828         'subcategory' => 'client+server',
1829         'detail'   => 'complete cycle (udp/53 dns)',
1830         'err_msg'  => 'could not complete SPA cycle',
1831         'function' => \&spa_cycle,
1832         'cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1833             "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1834             "$local_key_file --verbose --verbose " .
1835             "--gpg-recipient-key $gpg_server_key " .
1836             "--gpg-signer-key $gpg_client_key " .
1837             "--gpg-home-dir $gpg_client_home_dir",
1838         'fwknopd_cmdline'  => $default_server_gpg_args,
1839         'fw_rule_created' => $NEW_RULE_REQUIRED,
1840         'fw_rule_removed' => $NEW_RULE_REMOVED,
1841         'fatal'    => $NO
1842     },
1843
1844     {
1845         'category' => 'GnuPG (GPG) SPA',
1846         'subcategory' => 'client+server',
1847         'detail'   => 'replay attack detection',
1848         'err_msg'  => 'could not detect replay attack',
1849         'function' => \&replay_detection,
1850         'cmdline'  => $default_client_gpg_args,
1851         'fwknopd_cmdline'  => $default_server_gpg_args,
1852         'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1853         'fatal'    => $NO
1854     },
1855     {
1856         'category' => 'GnuPG (GPG) SPA',
1857         'subcategory' => 'client+server',
1858         'detail'   => 'replay detection (GnuPG prefix)',
1859         'err_msg'  => 'could not detect replay attack',
1860         'function' => \&replay_detection,
1861         'pkt_prefix' => 'hQ',
1862         'cmdline'  => $default_client_gpg_args,
1863         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1864             "$fwknopdCmd $default_server_conf_args $intf_str",
1865         'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1866         'fatal'    => $NO
1867     },
1868
1869     {
1870         'category' => 'GnuPG (GPG) SPA',
1871         'subcategory' => 'client+server',
1872         'detail'   => 'non-base64 altered SPA data',
1873         'err_msg'  => 'allowed improper SPA data',
1874         'function' => \&altered_non_base64_spa_data,
1875         'cmdline'  => $default_client_gpg_args,
1876         'fwknopd_cmdline'  => $default_server_gpg_args,
1877         'fatal'    => $NO
1878     },
1879     {
1880         'category' => 'GnuPG (GPG) SPA',
1881         'subcategory' => 'client+server',
1882         'detail'   => 'base64 altered SPA data',
1883         'err_msg'  => 'allowed improper SPA data',
1884         'function' => \&altered_base64_spa_data,
1885         'cmdline'  => $default_client_gpg_args,
1886         'fwknopd_cmdline'  => $default_server_gpg_args,
1887         'fatal'    => $NO
1888     },
1889     {
1890         'category' => 'GnuPG (GPG) SPA',
1891         'subcategory' => 'client+server',
1892         'detail'   => 'appended data to SPA pkt',
1893         'err_msg'  => 'allowed improper SPA data',
1894         'function' => \&appended_spa_data,
1895         'cmdline'  => $default_client_gpg_args,
1896         'fwknopd_cmdline'  => $default_server_gpg_args,
1897         'fatal'    => $NO
1898     },
1899     {
1900         'category' => 'GnuPG (GPG) SPA',
1901         'subcategory' => 'client+server',
1902         'detail'   => 'prepended data to SPA pkt',
1903         'err_msg'  => 'allowed improper SPA data',
1904         'function' => \&prepended_spa_data,
1905         'cmdline'  => $default_client_gpg_args,
1906         'fwknopd_cmdline'  => $default_server_gpg_args,
1907         'fatal'    => $NO
1908     },
1909     {
1910         'category' => 'GnuPG (GPG) SPA',
1911         'subcategory' => 'client+server',
1912         'detail'   => 'spoof username (tcp/22 ssh)',
1913         'err_msg'  => 'could not spoof username',
1914         'function' => \&spoof_username,
1915         'cmdline'  => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1916         'fwknopd_cmdline'  => $default_server_gpg_args,
1917         'fatal'    => $NO
1918     },
1919     {
1920         'category' => 'GnuPG (GPG) SPA',
1921         'subcategory' => 'server',
1922         'detail'   => 'digest cache structure',
1923         'err_msg'  => 'improper digest cache structure',
1924         'function' => \&digest_cache_structure,
1925         'fatal'    => $NO
1926     },
1927 );
1928
1929 if ($use_valgrind) {
1930     push @tests,
1931         {
1932             'category' => 'valgrind output',
1933             'subcategory' => 'flagged functions',
1934             'detail'   => '',
1935             'err_msg'  => 'could not parse flagged functions',
1936             'function' => \&parse_valgrind_flagged_functions,
1937             'fatal'    => $NO
1938         };
1939 }
1940
1941 my %test_keys = (
1942     'category'        => $REQUIRED,
1943     'subcategory'     => $OPTIONAL,
1944     'detail'          => $REQUIRED,
1945     'function'        => $REQUIRED,
1946     'binary'          => $OPTIONAL,
1947     'cmdline'         => $OPTIONAL,
1948     'fwknopd_cmdline' => $OPTIONAL,
1949     'fatal'           => $OPTIONAL,
1950     'exec_err'        => $OPTIONAL,
1951     'fw_rule_created' => $OPTIONAL,
1952     'fw_rule_removed' => $OPTIONAL,
1953     'server_conf'     => $OPTIONAL,
1954     'pkt_prefix'      => $OPTIONAL,
1955     'no_ip_check'     => $OPTIONAL,
1956     'positive_output_matches' => $OPTIONAL,
1957     'negative_output_matches' => $OPTIONAL,
1958     'server_positive_output_matches' => $OPTIONAL,
1959     'server_negative_output_matches' => $OPTIONAL,
1960     'replay_positive_output_matches' => $OPTIONAL,
1961     'replay_negative_output_matches' => $OPTIONAL,
1962 );
1963
1964 if ($diff_mode) {
1965     &diff_test_results();
1966     exit 0;
1967 }
1968
1969 ### make sure everything looks as expected before continuing
1970 &init();
1971
1972 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1973     "    args: @args_cp\n\n"
1974 );
1975
1976 ### save the results from any previous test suite run
1977 ### so that we can potentially compare them with --diff
1978 if ($saved_last_results) {
1979     &logr("    Saved results from previous run " .
1980         "to: ${output_dir}.last/\n\n");
1981 }
1982
1983 ### main loop through all of the tests
1984 for my $test_hr (@tests) {
1985     &run_test($test_hr);
1986 }
1987
1988 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1989
1990 copy $logfile, "$output_dir/$logfile" or die $!;
1991
1992 exit 0;
1993
1994 #===================== end main =======================
1995
1996 sub run_test() {
1997     my $test_hr = shift;
1998
1999     my $msg = "[$test_hr->{'category'}]";
2000     $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
2001     $msg .= " $test_hr->{'detail'}";
2002
2003     return unless &process_include_exclude($msg);
2004
2005     if ($list_mode) {
2006         print $msg, "\n";
2007         return;
2008     }
2009
2010     &dots_print($msg);
2011
2012     $executed++;
2013     $current_test_file  = "$output_dir/$executed.test";
2014     $server_test_file   = "$output_dir/${executed}_fwknopd.test";
2015
2016     &write_test_file("[+] TEST: $msg\n", $current_test_file);
2017     $test_hr->{'msg'} = $msg;
2018     if (&{$test_hr->{'function'}}($test_hr)) {
2019         &logr("pass ($executed)\n");
2020         $passed++;
2021     } else {
2022         &logr("fail ($executed)\n");
2023         $failed++;
2024
2025         if ($test_hr->{'fatal'} eq $YES) {
2026             die "[*] required test failed, exiting.";
2027         }
2028     }
2029
2030     return;
2031 }
2032
2033 sub process_include_exclude() {
2034     my $msg = shift;
2035
2036     ### inclusions/exclusions
2037     if (@tests_to_include) {
2038         my $found = 0;
2039         for my $test (@tests_to_include) {
2040             if ($msg =~ $test or ($use_valgrind
2041                     and $msg =~ /valgrind\soutput/)) {
2042                 $found = 1;
2043                 last;
2044             }
2045         }
2046         return 0 unless $found;
2047     }
2048     if (@tests_to_exclude) {
2049         my $found = 0;
2050         for my $test (@tests_to_exclude) {
2051             if ($msg =~ $test) {
2052                 $found = 1;
2053                 last;
2054             }
2055         }
2056         return 0 if $found;
2057     }
2058     return 1;
2059 }
2060
2061 sub diff_test_results() {
2062
2063     $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
2064     $diff_dir2 = $output_dir unless $diff_dir2;
2065
2066     die "[*] Need results from a previous run before running --diff"
2067         unless -d $diff_dir2;
2068     die "[*] Current results set does not exist." unless -d $diff_dir1;
2069
2070     my %current_tests  = ();
2071     my %previous_tests = ();
2072
2073     ### Only diff results for matching tests (parse the logfile to see which
2074     ### test numbers match across the two test cycles).
2075     &build_results_hash(\%current_tests, $diff_dir1);
2076     &build_results_hash(\%previous_tests, $diff_dir2);
2077
2078     for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
2079                 keys %current_tests) {
2080         my $current_result = $current_tests{$test_msg}{'pass_fail'};
2081         my $current_num    = $current_tests{$test_msg}{'num'};
2082         if (defined $previous_tests{$test_msg}) {
2083             print "[+] Checking: $test_msg\n";
2084             my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
2085             my $previous_num    = $previous_tests{$test_msg}{'num'};
2086             if ($current_result ne $previous_result) {
2087                 print " DIFF: **$current_result** $test_msg\n";
2088             }
2089
2090             &diff_results($previous_num, $current_num);
2091             print "\n";
2092         }
2093     }
2094
2095     exit 0;
2096 }
2097
2098 sub diff_results() {
2099     my ($previous_num, $current_num) = @_;
2100
2101     ### edit out any valgrind "==354==" prefixes
2102     my $valgrind_search_re = qr/^==\d+==\s/;
2103
2104     ### remove CMD timestamps
2105     my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
2106
2107     for my $file ("$diff_dir1/${previous_num}.test",
2108         "$diff_dir1/${previous_num}_fwknopd.test",
2109         "$diff_dir2/${current_num}.test",
2110         "$diff_dir2/${current_num}_fwknopd.test",
2111     ) {
2112         system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
2113         system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
2114     }
2115
2116     if (-e "$diff_dir1/${previous_num}.test"
2117             and -e "$diff_dir2/${current_num}.test") {
2118         system "diff -u $diff_dir1/${previous_num}.test " .
2119             "$diff_dir2/${current_num}.test";
2120     }
2121
2122     if (-e "$diff_dir1/${previous_num}_fwknopd.test"
2123             and -e "$diff_dir2/${current_num}_fwknopd.test") {
2124         system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
2125             "$diff_dir2/${current_num}_fwknopd.test";
2126     }
2127
2128     return;
2129 }
2130
2131 sub build_results_hash() {
2132     my ($hr, $dir) = @_;
2133
2134     open F, "< $dir/$logfile" or die $!;
2135     while (<F>) {
2136         if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
2137             $hr->{$1}{'pass_fail'} = $2;
2138             $hr->{$1}{'num'}       = $3;
2139         }
2140     }
2141     return;
2142 }
2143
2144 sub compile_warnings() {
2145
2146     ### 'make clean' as root
2147     return 0 unless &run_cmd('make -C .. clean',
2148         $cmd_out_tmp, $current_test_file);
2149
2150     if ($sudo_path) {
2151         my $username = getpwuid((stat($configure_path))[4]);
2152         die "[*] Could not determine $configure_path owner"
2153             unless $username;
2154
2155         return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
2156             $cmd_out_tmp, $current_test_file);
2157
2158     } else {
2159
2160         return 0 unless &run_cmd('make -C ..',
2161             $cmd_out_tmp, $current_test_file);
2162
2163     }
2164
2165     ### look for compilation warnings - something like:
2166     ###     warning: ‘test’ is used uninitialized in this function
2167     return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
2168         $MATCH_ANY, $current_test_file);
2169
2170     ### the new binaries should exist
2171     unless (-e $fwknopCmd and -x $fwknopCmd) {
2172         &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
2173             $current_test_file);
2174     }
2175     unless (-e $fwknopdCmd and -x $fwknopdCmd) {
2176         &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
2177             $current_test_file);
2178     }
2179
2180     return 1;
2181 }
2182
2183 sub make_distcheck() {
2184
2185     ### 'make clean' as root
2186     return 0 unless &run_cmd('make -C .. distcheck',
2187         $cmd_out_tmp, $current_test_file);
2188
2189     ### look for compilation warnings - something like:
2190     ###     warning: ‘test’ is used uninitialized in this function
2191     return 1 if &file_find_regex([qr/archives\sready\sfor\sdistribution/],
2192         $MATCH_ALL, $current_test_file);
2193
2194     return 0;
2195 }
2196
2197
2198 sub binary_exists() {
2199     my $test_hr = shift;
2200     return 0 unless $test_hr->{'binary'};
2201
2202     ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
2203     ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X)
2204
2205     if ($test_hr->{'binary'} =~ /libfko/) {
2206         unless (-e $test_hr->{'binary'}) {
2207             my $file = "$lib_dir/libfko.dylib";
2208             if (-e $file) {
2209                 $test_hr->{'binary'} = $file;
2210                 $libfko_bin = $file;
2211             } else {
2212                 for my $f (glob("$lib_dir/libfko.so*")) {
2213                     if (-e $f and -x $f) {
2214                         $test_hr->{'binary'} = $f;
2215                         $libfko_bin = $f;
2216                         last;
2217                     }
2218                 }
2219             }
2220         }
2221     }
2222
2223     return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
2224     return 1;
2225 }
2226
2227 sub expected_code_version() {
2228     my $test_hr = shift;
2229
2230     unless (-e '../VERSION') {
2231         &write_test_file("[-] ../VERSION file does not exist.\n",
2232             $current_test_file);
2233         return 0;
2234     }
2235
2236     open F, '< ../VERSION' or die $!;
2237     my $line = <F>;
2238     close F;
2239     if ($line =~ /(\d.*\d)/) {
2240         my $version = $1;
2241         return 0 unless &run_cmd($test_hr->{'cmdline'},
2242             $cmd_out_tmp, $current_test_file);
2243         return 1 if &file_find_regex([qr/$version/],
2244             $MATCH_ALL, $current_test_file);
2245     }
2246     return 0;
2247 }
2248
2249 sub client_send_spa_packet() {
2250     my $test_hr = shift;
2251
2252     &write_key('fwknoptest', $local_key_file);
2253
2254     return 0 unless &run_cmd($test_hr->{'cmdline'},
2255             $cmd_out_tmp, $current_test_file);
2256     return 0 unless &file_find_regex([qr/final\spacked/i],
2257         $MATCH_ALL, $current_test_file);
2258
2259     return 1;
2260 }
2261
2262 sub permissions_check() {
2263     my $test_hr = shift;
2264
2265     my $rv = 0;
2266     chmod 0777, $cf{'def'} or die $!;
2267     chmod 0777, $cf{'def_access'} or die $!;
2268
2269     $rv = &spa_cycle($test_hr);
2270
2271     chmod 0600, $cf{'def'} or die $!;
2272     chmod 0600, $cf{'def_access'} or die $!;
2273
2274     if ($test_hr->{'server_positive_output_matches'}) {
2275         $rv = 0 unless &file_find_regex(
2276             $test_hr->{'server_positive_output_matches'},
2277             $MATCH_ALL, $server_test_file);
2278     }
2279     return $rv;
2280 }
2281
2282 sub spa_cycle() {
2283     my $test_hr = shift;
2284
2285     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2286             = &client_server_interaction($test_hr, [], $USE_CLIENT);
2287
2288     if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
2289         $rv = 0 unless $fw_rule_created;
2290     } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
2291         $rv = 0 if $fw_rule_created;
2292     }
2293
2294     if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
2295         $rv = 0 unless $fw_rule_removed;
2296     } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
2297         $rv = 0 if $fw_rule_removed;
2298     }
2299
2300     if ($test_hr->{'server_positive_output_matches'}) {
2301         $rv = 0 unless &file_find_regex(
2302             $test_hr->{'server_positive_output_matches'},
2303             $MATCH_ALL, $server_test_file);
2304     }
2305
2306     if ($test_hr->{'server_negative_output_matches'}) {
2307         $rv = 0 if &file_find_regex(
2308             $test_hr->{'server_negative_output_matches'},
2309             $MATCH_ANY, $server_test_file);
2310     }
2311
2312     return $rv;
2313 }
2314
2315 sub spoof_username() {
2316     my $test_hr = shift;
2317
2318     my $rv = &spa_cycle($test_hr);
2319
2320     unless (&file_find_regex([qr/Username:\s*$spoof_user/],
2321             $MATCH_ALL, $current_test_file)) {
2322         $rv = 0;
2323     }
2324
2325     unless (&file_find_regex([qr/Username:\s*$spoof_user/],
2326             $MATCH_ALL, $server_test_file)) {
2327         $rv = 0;
2328     }
2329
2330     return $rv;
2331 }
2332
2333 sub replay_detection() {
2334     my $test_hr = shift;
2335
2336     ### do a complete SPA cycle and then parse the SPA packet out of the
2337     ### current test file and re-send
2338
2339     return 0 unless &spa_cycle($test_hr);
2340
2341     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2342
2343     unless ($spa_pkt) {
2344         &write_test_file("[-] could not get SPA packet " .
2345             "from file: $current_test_file\n",
2346             $current_test_file);
2347         return 0;
2348     }
2349
2350     if ($test_hr->{'pkt_prefix'}) {
2351         $spa_pkt = $test_hr->{'pkt_prefix'} . $spa_pkt;
2352     }
2353
2354     my @packets = (
2355         {
2356             'proto'  => 'udp',
2357             'port'   => $default_spa_port,
2358             'dst_ip' => $loopback_ip,
2359             'data'   => $spa_pkt,
2360         },
2361     );
2362
2363     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2364         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2365
2366     $rv = 0 unless $server_was_stopped;
2367
2368     if ($test_hr->{'replay_positive_output_matches'}) {
2369         $rv = 0 unless &file_find_regex(
2370             $test_hr->{'replay_positive_output_matches'},
2371             $MATCH_ALL, $server_test_file);
2372     }
2373
2374     if ($test_hr->{'replay_negative_output_matches'}) {
2375         $rv = 0 if &file_find_regex(
2376             $test_hr->{'replay_negative_output_matches'},
2377             $MATCH_ANY, $server_test_file);
2378     }
2379
2380     return $rv;
2381 }
2382
2383 sub digest_cache_structure() {
2384     my $test_hr = shift;
2385     my $rv = 1;
2386
2387     &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
2388
2389     if (&file_find_regex([qr/ASCII/i], $MATCH_ALL, $cmd_out_tmp)) {
2390
2391         ### the format should be:
2392         ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
2393         open F, "< $default_digest_file" or
2394             die "[*] could not open $default_digest_file: $!";
2395         while (<F>) {
2396             next if /^#/;
2397             next unless /\S/;
2398             unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
2399                 &write_test_file("[-] invalid digest.cache line: $_",
2400                     $current_test_file);
2401                 $rv = 0;
2402                 last;
2403             }
2404         }
2405         close F;
2406     } elsif (&file_find_regex([qr/dbm/i], $MATCH_ALL, $cmd_out_tmp)) {
2407         &write_test_file("[+] DBM digest file format, " .
2408             "assuming this is valid.\n", $current_test_file);
2409     } else {
2410         ### don't know what kind of file the digest.cache is
2411         &write_test_file("[-] unrecognized file type for " .
2412             "$default_digest_file.\n", $current_test_file);
2413         $rv = 0;
2414     }
2415
2416     if ($rv) {
2417         &write_test_file("[+] valid digest.cache structure.\n",
2418             $current_test_file);
2419     }
2420
2421     return $rv;
2422 }
2423
2424 sub server_bpf_ignore_packet() {
2425     my $test_hr = shift;
2426
2427     my $rv = 1;
2428     my $server_was_stopped = 0;
2429     my $fw_rule_created = 0;
2430     my $fw_rule_removed = 0;
2431
2432     unless (&client_send_spa_packet($test_hr)) {
2433         &write_test_file("[-] fwknop client execution error.\n",
2434             $current_test_file);
2435         $rv = 0;
2436     }
2437
2438     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2439
2440     unless ($spa_pkt) {
2441         &write_test_file("[-] could not get SPA packet " .
2442             "from file: $current_test_file\n", $current_test_file);
2443         return 0;
2444     }
2445
2446     my @packets = (
2447         {
2448             'proto'  => 'udp',
2449             'port'   => $default_spa_port,
2450             'dst_ip' => $loopback_ip,
2451             'data'   => $spa_pkt,
2452         },
2453     );
2454
2455     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2456         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2457
2458     unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
2459             $MATCH_ALL, $server_test_file)) {
2460         $rv = 0;
2461     }
2462
2463     return $rv;
2464 }
2465
2466 sub altered_non_base64_spa_data() {
2467     my $test_hr = shift;
2468
2469     my $rv = 1;
2470     my $server_was_stopped = 0;
2471     my $fw_rule_created = 0;
2472     my $fw_rule_removed = 0;
2473
2474     unless (&client_send_spa_packet($test_hr)) {
2475         &write_test_file("[-] fwknop client execution error.\n",
2476             $current_test_file);
2477         $rv = 0;
2478     }
2479
2480     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2481
2482     unless ($spa_pkt) {
2483         &write_test_file("[-] could not get SPA packet " .
2484             "from file: $current_test_file\n", $current_test_file);
2485         return 0;
2486     }
2487
2488     ### alter one byte (change to a ":")
2489     $spa_pkt =~ s|^(.{3}).|$1:|;
2490
2491     my @packets = (
2492         {
2493             'proto'  => 'udp',
2494             'port'   => $default_spa_port,
2495             'dst_ip' => $loopback_ip,
2496             'data'   => $spa_pkt,
2497         },
2498     );
2499
2500     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2501         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2502
2503     $rv = 0 unless $server_was_stopped;
2504
2505     return $rv;
2506 }
2507
2508 sub fuzzer() {
2509     my $test_hr = shift;
2510
2511     my $rv = 1;
2512     my $server_was_stopped = 0;
2513     my $fw_rule_created = 0;
2514     my $fw_rule_removed = 0;
2515
2516     my @packets = (
2517         {
2518             'proto'  => 'udp',
2519             'port'   => $default_spa_port,
2520             'dst_ip' => $loopback_ip,
2521             'data'   => $test_hr->{'fuzzing_pkt'},
2522         },
2523     );
2524
2525     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2526         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2527
2528     $rv = 0 unless $server_was_stopped;
2529
2530     if ($fw_rule_created) {
2531         &write_test_file("[-] new fw rule created.\n", $current_test_file);
2532         $rv = 0;
2533     } else {
2534         &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2535     }
2536
2537     if ($test_hr->{'server_positive_output_matches'}) {
2538         $rv = 0 unless &file_find_regex(
2539             $test_hr->{'server_positive_output_matches'},
2540             $MATCH_ALL, $server_test_file);
2541     }
2542
2543     return $rv;
2544 }
2545
2546 sub altered_base64_spa_data() {
2547     my $test_hr = shift;
2548
2549     my $rv = 1;
2550     my $server_was_stopped = 0;
2551     my $fw_rule_created = 0;
2552     my $fw_rule_removed = 0;
2553
2554     unless (&client_send_spa_packet($test_hr)) {
2555         &write_test_file("[-] fwknop client execution error.\n",
2556             $current_test_file);
2557         $rv = 0;
2558     }
2559
2560     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2561
2562     unless ($spa_pkt) {
2563         &write_test_file("[-] could not get SPA packet " .
2564             "from file: $current_test_file\n", $current_test_file);
2565         return 0;
2566     }
2567
2568     $spa_pkt =~ s|^(.{3}).|AAAA|;
2569
2570     my @packets = (
2571         {
2572             'proto'  => 'udp',
2573             'port'   => $default_spa_port,
2574             'dst_ip' => $loopback_ip,
2575             'data'   => $spa_pkt,
2576         },
2577     );
2578
2579     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2580         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2581
2582     $rv = 0 unless $server_was_stopped;
2583
2584     if ($fw_rule_created) {
2585         &write_test_file("[-] new fw rule created.\n", $current_test_file);
2586         $rv = 0;
2587     } else {
2588         &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2589     }
2590
2591     unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2592             $MATCH_ALL, $server_test_file)) {
2593         $rv = 0;
2594     }
2595
2596     return $rv;
2597 }
2598
2599 sub appended_spa_data() {
2600     my $test_hr = shift;
2601
2602     my $rv = 1;
2603     my $server_was_stopped = 0;
2604     my $fw_rule_created = 0;
2605     my $fw_rule_removed = 0;
2606
2607     unless (&client_send_spa_packet($test_hr)) {
2608         &write_test_file("[-] fwknop client execution error.\n",
2609             $current_test_file);
2610         $rv = 0;
2611     }
2612
2613     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2614
2615     unless ($spa_pkt) {
2616         &write_test_file("[-] could not get SPA packet " .
2617             "from file: $current_test_file\n", $current_test_file);
2618         return 0;
2619     }
2620
2621     $spa_pkt .= 'AAAA';
2622
2623     my @packets = (
2624         {
2625             'proto'  => 'udp',
2626             'port'   => $default_spa_port,
2627             'dst_ip' => $loopback_ip,
2628             'data'   => $spa_pkt,
2629         },
2630     );
2631
2632     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2633         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2634
2635     $rv = 0 unless $server_was_stopped;
2636
2637     if ($fw_rule_created) {
2638         &write_test_file("[-] new fw rule created.\n", $current_test_file);
2639         $rv = 0;
2640     } else {
2641         &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2642     }
2643
2644     unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2645             $MATCH_ALL, $server_test_file)) {
2646         $rv = 0;
2647     }
2648
2649     return $rv;
2650 }
2651
2652 sub prepended_spa_data() {
2653     my $test_hr = shift;
2654
2655     my $rv = 1;
2656     my $server_was_stopped = 0;
2657     my $fw_rule_created = 0;
2658     my $fw_rule_removed = 0;
2659
2660     unless (&client_send_spa_packet($test_hr)) {
2661         &write_test_file("[-] fwknop client execution error.\n",
2662             $current_test_file);
2663         $rv = 0;
2664     }
2665
2666     my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2667
2668     unless ($spa_pkt) {
2669         &write_test_file("[-] could not get SPA packet " .
2670             "from file: $current_test_file\n", $current_test_file);
2671         return 0;
2672     }
2673
2674     $spa_pkt = 'AAAA' . $spa_pkt;
2675
2676     my @packets = (
2677         {
2678             'proto'  => 'udp',
2679             'port'   => $default_spa_port,
2680             'dst_ip' => $loopback_ip,
2681             'data'   => $spa_pkt,
2682         },
2683     );
2684
2685     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2686         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2687
2688     $rv = 0 unless $server_was_stopped;
2689
2690     if ($fw_rule_created) {
2691         &write_test_file("[-] new fw rule created.\n", $current_test_file);
2692         $rv = 0;
2693     } else {
2694         &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2695     }
2696
2697     unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2698             $MATCH_ALL, $server_test_file)) {
2699         $rv = 0;
2700     }
2701
2702     return $rv;
2703 }
2704
2705 sub server_start() {
2706     my $test_hr = shift;
2707
2708     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2709         = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2710
2711     unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
2712             $MATCH_ALL, $server_test_file)) {
2713         $rv = 0;
2714     }
2715
2716     $rv = 0 unless $server_was_stopped;
2717
2718     return $rv;
2719 }
2720
2721 sub server_stop() {
2722     my $test_hr = shift;
2723
2724     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2725         = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2726
2727     $rv = 0 unless $server_was_stopped;
2728
2729     return $rv;
2730 }
2731
2732 sub server_packet_limit() {
2733     my $test_hr = shift;
2734
2735     my @packets = (
2736         {
2737             'proto'  => 'udp',
2738             'port'   => $default_spa_port,
2739             'dst_ip' => $loopback_ip,
2740             'data'   => 'A'x700,
2741         },
2742     );
2743
2744     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2745         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2746
2747     if (&is_fwknopd_running()) {
2748         &stop_fwknopd();
2749         $rv = 0;
2750     }
2751
2752     unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2753             $MATCH_ALL, $server_test_file)) {
2754         $rv = 0;
2755     }
2756
2757     unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2758             $MATCH_ALL, $server_test_file)) {
2759         $rv = 0;
2760     }
2761
2762     return $rv;
2763 }
2764
2765 sub server_ignore_small_packets() {
2766     my $test_hr = shift;
2767
2768     my @packets = (
2769         {
2770             'proto'  => 'udp',
2771             'port'   => $default_spa_port,
2772             'dst_ip' => $loopback_ip,
2773             'data'   => 'A'x120,  ### < MIN_SPA_DATA_SIZE
2774         },
2775     );
2776
2777     my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2778         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2779
2780     sleep 2;
2781
2782     if (&is_fwknopd_running()) {
2783         &stop_fwknopd();
2784         $rv = 0;
2785     }
2786
2787     return $rv;
2788 }
2789
2790 sub client_server_interaction() {
2791     my ($test_hr, $pkts_hr, $spa_client_flag) = @_;
2792
2793     my $rv = 1;
2794     my $server_was_stopped = 1;
2795     my $fw_rule_created = 1;
2796     my $fw_rule_removed = 0;
2797
2798     ### start fwknopd to monitor for the SPA packet over the loopback interface
2799     my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2800
2801     ### give fwknopd a chance to parse its config and start sniffing
2802     ### on the loopback interface
2803     if ($use_valgrind) {
2804         sleep 3;
2805     } else {
2806         sleep 2;
2807     }
2808
2809     ### send the SPA packet(s) to the server either manually using IO::Socket or
2810     ### with the fwknopd client
2811     if ($spa_client_flag == $USE_CLIENT) {
2812         unless (&client_send_spa_packet($test_hr)) {
2813             &write_test_file("[-] fwknop client execution error.\n",
2814                 $current_test_file);
2815             $rv = 0;
2816         }
2817     } else {
2818         &send_packets($pkts_hr);
2819     }
2820
2821     ### check to see if the SPA packet resulted in a new fw access rule
2822     my $ctr = 0;
2823     while (not &is_fw_rule_active($test_hr)) {
2824         &write_test_file("[-] new fw rule does not exist.\n",
2825             $current_test_file);
2826         $ctr++;
2827         last if $ctr == 3;
2828         sleep 1;
2829     }
2830     if ($ctr == 3) {
2831         $fw_rule_created = 0;
2832         $fw_rule_removed = 0;
2833     }
2834
2835     &time_for_valgrind() if $use_valgrind;
2836
2837     if ($fw_rule_created) {
2838         sleep 3;  ### allow time for rule time out.
2839         if (&is_fw_rule_active($test_hr)) {
2840             &write_test_file("[-] new fw rule not timed out.\n",
2841                 $current_test_file);
2842             $rv = 0;
2843         } else {
2844             &write_test_file("[+] new fw rule timed out.\n",
2845                 $current_test_file);
2846             $fw_rule_removed = 1;
2847         }
2848     }
2849
2850     if (&is_fwknopd_running()) {
2851         &stop_fwknopd();
2852         unless (&file_find_regex([qr/Got\sSIGTERM/],
2853                 $MATCH_ALL, $server_test_file)) {
2854             $server_was_stopped = 0;
2855         }
2856     } else {
2857         &write_test_file("[-] server is not running.\n",
2858             $current_test_file);
2859         $server_was_stopped = 0;
2860     }
2861
2862     return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2863 }
2864
2865 sub get_spa_packet_from_file() {
2866     my $file = shift;
2867
2868     my $spa_pkt = '';
2869
2870     my $found_trigger_line = 0;
2871     open F, "< $file" or die "[*] Could not open file $file: $!";
2872     while (<F>) {
2873         if (/final\spacked/i) {
2874             $found_trigger_line = 1;
2875             next;
2876         }
2877         next unless $found_trigger_line;
2878
2879         ### the next line with non whitespace is the SPA packet
2880         if (/(\S+)/) {
2881             $spa_pkt = $1;
2882             last;
2883         }
2884     }
2885     close F;
2886
2887     return $spa_pkt;
2888 }
2889
2890 sub send_packets() {
2891     my $pkts_ar = shift;
2892
2893     open F, ">> $current_test_file" or die $!;
2894     print F "[+] send_packets(): Sending the following packets...\n";
2895     print F Dumper $pkts_ar;
2896     close F;
2897
2898     for my $pkt_hr (@$pkts_ar) {
2899         if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2900             my $socket = IO::Socket::INET->new(
2901                 PeerAddr => $pkt_hr->{'dst_ip'},
2902                 PeerPort => $pkt_hr->{'port'},
2903                 Proto    => $pkt_hr->{'proto'},
2904                 Timeout  => 1
2905             ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2906                 "socket to $pkt_hr->{'dst_ip'}: $!";
2907
2908             $socket->send($pkt_hr->{'data'});
2909             undef $socket;
2910
2911         } elsif ($pkt_hr->{'proto'} eq 'http') {
2912             ### FIXME
2913         } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2914             ### FIXME
2915         }
2916
2917         sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2918     }
2919     return;
2920 }
2921
2922 sub generic_exec() {
2923     my $test_hr = shift;
2924
2925     my $rv = 1;
2926
2927     my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2928                 $cmd_out_tmp, $current_test_file);
2929
2930     if ($test_hr->{'exec_err'} eq $YES) {
2931         $rv = 0 if $exec_rv;
2932     } else {
2933         $rv = 0 unless $exec_rv;
2934     }
2935
2936     if ($test_hr->{'positive_output_matches'}) {
2937         $rv = 0 unless &file_find_regex(
2938             $test_hr->{'positive_output_matches'},
2939             $MATCH_ALL, $current_test_file);
2940     }
2941
2942     if ($test_hr->{'negative_output_matches'}) {
2943         $rv = 0 if &file_find_regex(
2944             $test_hr->{'negative_output_matches'},
2945             $MATCH_ANY, $current_test_file);
2946     }
2947
2948     return $rv;
2949 }
2950
2951 ### check for PIE
2952 sub pie_binary() {
2953     my $test_hr = shift;
2954     return 0 unless $test_hr->{'binary'};
2955     &run_cmd("./hardening-check $test_hr->{'binary'}",
2956             $cmd_out_tmp, $current_test_file);
2957     return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2958         $MATCH_ALL, $current_test_file);
2959     return 1;
2960 }
2961
2962 ### check for stack protection
2963 sub stack_protected_binary() {
2964     my $test_hr = shift;
2965     return 0 unless $test_hr->{'binary'};
2966     &run_cmd("./hardening-check $test_hr->{'binary'}",
2967             $cmd_out_tmp, $current_test_file);
2968     return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2969         $MATCH_ALL, $current_test_file);
2970     return 1;
2971 }
2972
2973 ### check for fortified source functions
2974 sub fortify_source_functions() {
2975     my $test_hr = shift;
2976     return 0 unless $test_hr->{'binary'};
2977     &run_cmd("./hardening-check $test_hr->{'binary'}",
2978             $cmd_out_tmp, $current_test_file);
2979     return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2980         $MATCH_ALL, $current_test_file);
2981     return 1;
2982 }
2983
2984 ### check for read-only relocations
2985 sub read_only_relocations() {
2986     my $test_hr = shift;
2987     return 0 unless $test_hr->{'binary'};
2988     &run_cmd("./hardening-check $test_hr->{'binary'}",
2989             $cmd_out_tmp, $current_test_file);
2990     return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2991         $MATCH_ALL, $current_test_file);
2992     return 1;
2993 }
2994
2995 ### check for immediate binding
2996 sub immediate_binding() {
2997     my $test_hr = shift;
2998     return 0 unless $test_hr->{'binary'};
2999     &run_cmd("./hardening-check $test_hr->{'binary'}",
3000             $cmd_out_tmp, $current_test_file);
3001     return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
3002         $MATCH_ALL, $current_test_file);
3003     return 1;
3004 }
3005
3006 sub specs() {
3007
3008      &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
3009             "$default_server_conf_args --fw-list-all",
3010             $cmd_out_tmp, $current_test_file);
3011
3012     my $have_gpgme = 0;
3013
3014     for my $cmd (
3015         'uname -a',
3016         'uptime',
3017         'ifconfig -a',
3018         'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
3019         'if [ `which iptables` ]; then iptables -V; fi',
3020         'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
3021         'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
3022         'if [ `which gpg` ]; then gpg --version; fi',
3023         'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
3024         "ldd $fwknopCmd",
3025         "ldd $fwknopdCmd",
3026         "ldd $libfko_bin",
3027         'ls -l /usr/lib/*pcap*',
3028         'ls -l /usr/local/lib/*pcap*',
3029         'ls -l /usr/lib/*fko*',
3030         'ls -l /usr/local/lib/*fko*',
3031     ) {
3032         &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
3033
3034         if ($cmd =~ /^ldd/) {
3035             $have_gpgme++ if &file_find_regex([qr/gpgme/],
3036                 $MATCH_ALL, $cmd_out_tmp);
3037         }
3038     }
3039
3040     ### all three of fwknop/fwknopd/libfko must link against gpgme in order
3041     ### to enable gpg tests
3042     unless ($have_gpgme == 3) {
3043         push @tests_to_exclude, qr/GPG/;
3044     }
3045
3046     return 1;
3047 }
3048
3049 sub time_for_valgrind() {
3050     my $ctr = 0;
3051     while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
3052             "grep valgrind |grep -v perl | grep -v grep",
3053             $cmd_out_tmp, $current_test_file)) {
3054         $ctr++;
3055         last if $ctr == 5;
3056         sleep 1;
3057     }
3058     return;
3059 }
3060
3061 sub anonymize_results() {
3062     my $rv = 0;
3063     die "[*] $output_dir does not exist" unless -d $output_dir;
3064     die "[*] $logfile does not exist, has $0 been executed?"
3065         unless -e $logfile;
3066     if (-e $tarfile) {
3067         unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
3068     }
3069
3070     ### remove non-loopback IP addresses
3071     my $search_re = qr/\b127\.0\.0\.1\b/;
3072     system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
3073     $search_re = qr/\b127\.0\.0\.2\b/;
3074     system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
3075     $search_re = qr/\b0\.0\.0\.0\b/;
3076     system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
3077     $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
3078     system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
3079     system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
3080     system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
3081     system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
3082
3083     ### remove hostname from any uname output
3084     $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
3085     system "perl -p -i -e 'undef \$/; s|$search_re" .
3086         "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
3087
3088     $search_re = qr/uname=\x27(\S+)\s+\S+/;
3089     system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
3090
3091     ### create tarball
3092     system "tar cvfz $tarfile $logfile $output_dir";
3093     print "[+] Anonymized test results file: $tarfile\n";
3094     if (-e $tarfile) {
3095         $rv = 1;
3096     }
3097     return $rv;
3098 }
3099
3100
3101 sub write_pid() {
3102     my $test_hr = shift;
3103
3104     open F, "> $default_pid_file" or die $!;
3105     print F "1\n";
3106     close F;
3107
3108     &server_start($test_hr);
3109
3110     open F, "< $default_pid_file" or die $!;
3111     my $pid = <F>;
3112     chomp $pid;
3113     close F;
3114
3115     if ($pid != 1) {
3116         return 1;
3117     }
3118
3119     return 0;
3120 }
3121
3122 sub start_fwknopd() {
3123     my $test_hr = shift;
3124
3125     &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
3126
3127     my $pid = fork();
3128     die "[*] Could not fork: $!" unless defined $pid;
3129
3130     if ($pid == 0) {
3131
3132         ### we are the child, so start fwknopd
3133         exit &run_cmd($test_hr->{'fwknopd_cmdline'},
3134             $server_cmd_tmp, $server_test_file);
3135     }
3136     return $pid;
3137 }
3138
3139 sub write_key() {
3140     my ($key, $file) = @_;
3141
3142     open K, "> $file" or die "[*] Could not open $file: $!";
3143     print K "$loopback_ip: $key\n";
3144     print K "localhost: $key\n";
3145     print K "some.host.through.proxy.com: $key\n";
3146     close K;
3147     return;
3148 }
3149
3150 sub dump_pids() {
3151     open C, ">> $current_test_file"
3152         or die "[*] Could not open $current_test_file: $!";
3153     print C "\n" . localtime() . " [+] PID dump:\n";
3154     close C;
3155     &run_cmd("ps auxww | grep knop |grep -v grep",
3156         $cmd_out_tmp, $current_test_file);
3157     return;
3158 }
3159
3160 sub run_cmd() {
3161     my ($cmd, $cmd_out, $file) = @_;
3162
3163     if (-e $file) {
3164         open F, ">> $file"
3165             or die "[*] Could not open $file: $!";
3166         print F localtime() . " CMD: $cmd\n";
3167         close F;
3168     } else {
3169         open F, "> $file"
3170             or die "[*] Could not open $file: $!";
3171         print F localtime() . " CMD: $cmd\n";
3172         close F;
3173     }
3174
3175     ### copy original file descriptors (credit: Perl Cookbook)
3176     open OLDOUT, ">&STDOUT";
3177     open OLDERR, ">&STDERR";
3178
3179     ### redirect command output
3180     open STDOUT, "> $cmd_out" or die "[*] Could not redirect stdout: $!";
3181     open STDERR, ">&STDOUT"   or die "[*] Could not dup stdout: $!";
3182
3183     my $rv = ((system $cmd) >> 8);
3184
3185     close STDOUT or die "[*] Could not close STDOUT: $!";
3186     close STDERR or die "[*] Could not close STDERR: $!";
3187
3188     ### restore original filehandles
3189     open STDERR, ">&OLDERR" or die "[*] Could not restore stderr: $!";
3190     open STDOUT, ">&OLDOUT" or die "[*] Could not restore stdout: $!";
3191
3192     ### close the old copies
3193     close OLDOUT or die "[*] Could not close OLDOUT: $!";
3194     close OLDERR or die "[*] Could not close OLDERR: $!";
3195
3196     open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
3197     my @cmd_lines = <C>;
3198     close C;
3199
3200     open F, ">> $file" or die "[*] Could not open $file: $!";
3201     print F $_ for @cmd_lines;
3202     close F;
3203
3204     if ($rv == 0) {
3205         return 1;
3206     }
3207     return 0;
3208 }
3209
3210 sub dots_print() {
3211     my $msg = shift;
3212     &logr($msg);
3213     my $dots = '';
3214     for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
3215         $dots .= '.';
3216     }
3217     &logr($dots);
3218     return;
3219 }
3220
3221 sub init() {
3222
3223     $|++; ### turn off buffering
3224
3225     $< == 0 && $> == 0 or
3226         die "[*] $0: You must be root (or equivalent ",
3227             "UID 0 account) to effectively test fwknop";
3228
3229     ### validate test hashes
3230     my $hash_num = 0;
3231     for my $test_hr (@tests) {
3232         for my $key (keys %test_keys) {
3233             if ($test_keys{$key} == $REQUIRED) {
3234                 die "[*] Missing '$key' element in hash: $hash_num"
3235                     unless defined $test_hr->{$key};
3236             } else {
3237                 $test_hr->{$key} = '' unless defined $test_hr->{$key};
3238             }
3239         }
3240         $hash_num++;
3241     }
3242
3243     if ($use_valgrind) {
3244         die "[*] $valgrindCmd exec problem, use --valgrind-path"
3245             unless -e $valgrindCmd and -x $valgrindCmd;
3246     }
3247
3248     die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
3249     die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
3250
3251     for my $name (keys %cf) {
3252         die "[*] $cf{$name} does not exist" unless -e $cf{$name};
3253         chmod 0600, $cf{$name} or die "[*] Could not chmod 0600 $cf{$name}";
3254     }
3255
3256     if (-d $output_dir) {
3257         if (-d "${output_dir}.last") {
3258             rmtree "${output_dir}.last"
3259                 or die "[*] rmtree ${output_dir}.last $!";
3260         }
3261         mkdir "${output_dir}.last"
3262             or die "[*] ${output_dir}.last: $!";
3263         for my $file (glob("$output_dir/*.test")) {
3264             if ($file =~ m|.*/(.*)|) {
3265                 copy $file, "${output_dir}.last/$1" or die $!;
3266             }
3267         }
3268         if (-e "$output_dir/init") {
3269             copy "$output_dir/init", "${output_dir}.last/init";
3270         }
3271         if (-e $logfile) {
3272             copy $logfile, "${output_dir}.last/$logfile" or die $!;
3273         }
3274         $saved_last_results = 1;
3275     } else {
3276         mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
3277     }
3278
3279     if (-d $run_dir) {
3280         rmtree $run_dir or die $!;
3281     }
3282     mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
3283
3284     for my $file (glob("$output_dir/*.test")) {
3285         unlink $file or die "[*] Could not unlink($file)";
3286     }
3287     if (-e "$output_dir/init") {
3288         unlink "$output_dir/init" or die $!;
3289     }
3290
3291     if (-e $logfile) {
3292         unlink $logfile or die $!;
3293     }
3294
3295     if ($test_include) {
3296         for my $re (split /\s*,\s*/, $test_include) {
3297             push @tests_to_include, qr/$re/;
3298         }
3299     }
3300     if ($test_exclude) {
3301         for my $re (split /\s*,\s*/, $test_exclude) {
3302             push @tests_to_exclude, qr/$re/;
3303         }
3304     }
3305
3306     ### make sure no fwknopd instance is currently running
3307     die "[*] Please stop the running fwknopd instance."
3308         if &is_fwknopd_running();
3309
3310     unless ($enable_recompilation_warnings_check) {
3311         push @tests_to_exclude, qr/recompilation/;
3312     }
3313
3314     unless ($enable_make_distcheck) {
3315         push @tests_to_exclude, qr/distcheck/;
3316     }
3317
3318     unless ($enable_client_ip_resolve_test) {
3319         push @tests_to_exclude, qr/IP resolve/;
3320     }
3321
3322     $sudo_path = &find_command('sudo');
3323
3324     unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
3325         ### disable compilation checks
3326         push @tests_to_exclude, qr/recompilation/;
3327     }
3328
3329     open UNAME, "uname |" or die "[*] Could not execute uname: $!";
3330     while (<UNAME>) {
3331         if (/linux/i) {
3332             $platform = $LINUX;
3333             last;
3334         } elsif (/freebsd/i) {
3335             $platform = $FREEBSD;
3336             last;
3337         }
3338     }
3339     close UNAME;
3340
3341     unless ($platform eq $LINUX) {
3342         push @tests_to_exclude, qr/NAT/;
3343     }
3344     unless ($platform eq $FREEBSD or $platform eq $MACOSX) {
3345         push @tests_to_exclude, qr|active/expire sets|;
3346     }
3347
3348     if (-e $default_digest_file) {
3349         unlink $default_digest_file;
3350     }
3351
3352     return;
3353 }
3354
3355 sub identify_loopback_intf() {
3356     return if $loopback_intf;
3357
3358     ### Linux:
3359
3360     ### lo    Link encap:Local Loopback
3361     ###       inet addr:127.0.0.1  Mask:255.0.0.0
3362     ###       inet6 addr: ::1/128 Scope:Host
3363     ###       UP LOOPBACK RUNNING  MTU:16436  Metric:1
3364     ###       RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
3365     ###       TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
3366     ###       collisions:0 txqueuelen:0
3367     ###       RX bytes:101110617 (101.1 MB)  TX bytes:101110617 (101.1 MB)
3368
3369     ### Freebsd:
3370
3371     ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
3372     ###         options=3<RXCSUM,TXCSUM>
3373     ###         inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
3374     ###         inet6 ::1 prefixlen 128
3375     ###         inet 127.0.0.1 netmask 0xff000000
3376     ###         nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
3377
3378     my $intf = '';
3379     my $found_loopback_intf = 0;
3380
3381     my $cmd = 'ifconfig -a';
3382     open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
3383     while (<C>) {
3384         if (/^(\S+?):?\s+.*loopback/i) {
3385             $intf = $1;
3386             next;
3387         }
3388         if (/^\S/ and $intf and not $found_loopback_intf) {
3389             ### should not happen
3390             last;
3391         }
3392         if ($intf and /\b127\.0\.0\.1\b/) {
3393             $found_loopback_intf = 1;
3394             last;
3395         }
3396     }
3397     close C;
3398
3399     die "[*] could not determine loopback interface, use --loopback <name>"
3400         unless $found_loopback_intf;
3401
3402     $loopback_intf = $intf;
3403
3404     return;
3405 }
3406
3407 sub parse_valgrind_flagged_functions() {
3408     for my $file (glob("$output_dir/*.test")) {
3409         my $type = 'server';
3410         $type = 'client' if $file =~ /\d\.test/;
3411         open F, "< $file" or die $!;
3412         while (<F>) {
3413             ### ==30969==    by 0x4E3983A: fko_set_username (fko_user.c:65)
3414             if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
3415                 $valgrind_flagged_fcns{$type}{"$1 $2"}++;
3416                 $valgrind_flagged_fcns_unique{$type}{$1}++;
3417             }
3418         }
3419         close F;
3420     }
3421
3422     open F, ">> $current_test_file" or die $!;
3423     for my $type ('client', 'server') {
3424         print F "\n[+] fwknop $type functions (unique view):\n";
3425         next unless defined $valgrind_flagged_fcns_unique{$type};
3426         for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
3427                 <=> $valgrind_flagged_fcns_unique{$type}{$a}}
3428                 keys %{$valgrind_flagged_fcns_unique{$type}}) {
3429             printf F "    %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
3430         }
3431         print F "\n[+] fwknop $type functions (with call line numbers):\n";
3432         for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
3433                 <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
3434             printf F "    %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
3435         }
3436         next unless defined $valgrind_flagged_fcns{$type};
3437
3438     }
3439     close F;
3440     return 1;
3441 }
3442
3443 sub is_fw_rule_active() {
3444     my $test_hr = shift;
3445
3446     my $conf_args = $default_server_conf_args;
3447
3448     if ($test_hr->{'server_conf'}) {
3449         $conf_args = "-c $test_hr->{'server_conf'} -a $cf{'def_access'} " .
3450             "-d $default_digest_file -p $default_pid_file";
3451     }
3452
3453     if ($test_hr->{'no_ip_check'}) {
3454         return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3455                 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep _exp_},
3456                 $cmd_out_tmp, $current_test_file);
3457     } else {
3458         return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3459                 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
3460                 $cmd_out_tmp, $current_test_file);
3461     }
3462
3463     return 0;
3464 }
3465
3466 sub is_fwknopd_running() {
3467
3468     sleep 2 if $use_valgrind;
3469
3470     &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
3471         "--status", $cmd_out_tmp, $current_test_file);
3472
3473     return 1 if &file_find_regex([qr/Detected\sfwknopd\sis\srunning/i],
3474             $MATCH_ALL, $cmd_out_tmp);
3475
3476     return 0;
3477 }
3478
3479 sub stop_fwknopd() {
3480
3481     &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3482         "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
3483
3484     if ($use_valgrind) {
3485         &time_for_valgrind();
3486     } else {
3487         sleep 1;
3488     }
3489
3490     return;
3491 }
3492
3493 sub file_find_regex() {
3494     my ($re_ar, $match_style, $file) = @_;
3495
3496     my $found_all_regexs = 1;
3497     my $found_single_match = 0;
3498     my @write_lines = ();
3499     my @file_lines = ();
3500
3501     open F, "< $file" or die "[*] Could not open $file: $!";
3502     while (<F>) {
3503         push @file_lines, $_;
3504     }
3505     close F;
3506
3507     for my $re (@$re_ar) {
3508         my $matched = 0;
3509         for my $line (@file_lines) {
3510             if ($line =~ $re) {
3511                 push @write_lines, "[.] file_find_regex() " .
3512                     "Matched '$re' with line: $line";
3513                 $matched = 1;
3514                 $found_single_match = 1;
3515             }
3516         }
3517         unless ($matched) {
3518             push @write_lines, "[.] file_find_regex() " .
3519                 "Did not match regex '$re' from regexs: '@$re_ar' " .
3520                 "within file: $file\n";
3521             $found_all_regexs = 0;
3522         }
3523     }
3524
3525     for my $line (@write_lines) {
3526         &write_test_file($line, $file);
3527     }
3528
3529     if ($match_style == $MATCH_ANY) {
3530         return $found_single_match;
3531     }
3532
3533     return $found_all_regexs;
3534 }
3535
3536 sub find_command() {
3537     my $cmd = shift;
3538
3539     my $path = '';
3540     open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
3541     while (<C>) {
3542         if (m|^(/.*$cmd)$|) {
3543             $path = $1;
3544             last;
3545         }
3546     }
3547     close C;
3548     return $path;
3549 }
3550
3551 sub write_test_file() {
3552     my ($msg, $file) = @_;
3553
3554     if (-e $file) {
3555         open F, ">> $file"
3556             or die "[*] Could not open $file: $!";
3557         print F $msg;
3558         close F;
3559     } else {
3560         open F, "> $file"
3561             or die "[*] Could not open $file: $!";
3562         print F $msg;
3563         close F;
3564     }
3565     return;
3566 }
3567
3568 sub logr() {
3569     my $msg = shift;
3570     print STDOUT $msg;
3571     open F, ">> $logfile" or die $!;
3572     print F $msg;
3573     close F;
3574     return;
3575 }
3576
3577 sub usage() {
3578     print <<_HELP_;
3579
3580 [+] $0 <options>
3581
3582     -A   --Anonymize-results      - Prepare anonymized results at:
3583                                     $tarfile
3584     --diff                        - Compare the results of one test run to
3585                                     another.  By default this compares output
3586                                     in ${output_dir}.last to $output_dir
3587     --diff-dir1=<path>            - Left hand side of diff directory path,
3588                                     default is: ${output_dir}.last
3589     --diff-dir2=<path>            - Right hand side of diff directory path,
3590                                     default is: $output_dir
3591     --include=<regex>             - Specify a regex to be used over test
3592                                     names that must match.
3593     --exclude=<regex>             - Specify a regex to be used over test
3594                                     names that must not match.
3595     --enable-recompile            - Recompile fwknop sources and look for
3596                                     compilation warnings.
3597     --enable-valgrind             - Run every test underneath valgrind.
3598     --List                        - List test names.
3599     --loopback-intf=<intf>        - Specify loopback interface name (default
3600                                     depends on the OS where the test suite
3601                                     is executed).
3602     --output-dir=<path>           - Path to output directory, default is:
3603                                     $output_dir
3604     --fwknop-path=<path>          - Path to fwknop binary, default is:
3605                                     $fwknopCmd
3606     --fwknopd-path=<path>         - Path to fwknopd binary, default is:
3607                                     $fwknopdCmd
3608     --libfko-path=<path>          - Path to libfko, default is:
3609                                     $libfko_bin
3610     --valgrind-path=<path>        - Path to valgrind, default is:
3611                                     $valgrindCmd
3612     -h   --help                   - Display usage on STDOUT and exit.
3613
3614 _HELP_
3615     exit 0;
3616 }