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