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