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