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