7 use Getopt::Long 'GetOptions';
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';
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";
24 'nat' => "$conf_dir/nat_fwknopd.conf",
25 'def' => "$conf_dir/default_fwknopd.conf",
26 'def_access' => "$conf_dir/default_access.conf",
27 'exp_access' => "$conf_dir/expired_stanza_access.conf",
28 'future_exp_access' => "$conf_dir/future_expired_stanza_access.conf",
29 'exp_epoch_access' => "$conf_dir/expired_epoch_stanza_access.conf",
30 'invalid_exp_access' => "$conf_dir/invalid_expire_access.conf",
31 'force_nat_access' => "$conf_dir/force_nat_access.conf",
32 'local_nat' => "$conf_dir/local_nat_fwknopd.conf",
33 'ipfw_active_expire' => "$conf_dir/ipfw_active_expire_equal_fwknopd.conf",
34 'dual_key_access' => "$conf_dir/dual_key_usage_access.conf",
35 'gpg_access' => "$conf_dir/gpg_access.conf",
36 'gpg_no_pw_access' => "$conf_dir/gpg_no_pw_access.conf",
37 'open_ports_access' => "$conf_dir/open_ports_access.conf",
38 'multi_gpg_access' => "$conf_dir/multi_gpg_access.conf",
39 'multi_stanza_access' => "$conf_dir/multi_stanzas_access.conf",
40 'broken_keys_access' => "$conf_dir/multi_stanzas_with_broken_keys.conf",
41 'open_ports_mismatch' => "$conf_dir/mismatch_open_ports_access.conf",
42 'require_user_access' => "$conf_dir/require_user_access.conf",
43 'user_mismatch_access' => "$conf_dir/mismatch_user_access.conf",
44 'require_src_access' => "$conf_dir/require_src_access.conf",
45 'no_src_match' => "$conf_dir/no_source_match_access.conf",
46 'no_subnet_match' => "$conf_dir/no_subnet_source_match_access.conf",
47 'no_multi_src' => "$conf_dir/no_multi_source_match_access.conf",
48 'multi_src_access' => "$conf_dir/multi_source_match_access.conf",
49 'ip_src_match' => "$conf_dir/ip_source_match_access.conf",
50 'subnet_src_match' => "$conf_dir/ip_source_match_access.conf",
53 my $default_digest_file = "$run_dir/digest.cache";
54 my $default_pid_file = "$run_dir/fwknopd.pid";
56 my $fwknopCmd = '../client/.libs/fwknop';
57 my $fwknopdCmd = '../server/.libs/fwknopd';
58 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
59 my $valgrindCmd = '/usr/bin/valgrind';
61 my $gpg_server_key = '361BBAD4';
62 my $gpg_client_key = '6A3FAD56';
64 my $loopback_ip = '127.0.0.1';
65 my $fake_ip = '127.0.0.2';
66 my $internal_nat_host = '192.168.1.2';
67 my $force_nat_host = '192.168.1.123';
68 my $default_spa_port = 62201;
69 my $non_std_spa_port = 12345;
71 my $spoof_user = 'testuser';
72 #================== end config ===================
77 my $test_include = '';
78 my @tests_to_include = ();
79 my $test_exclude = '';
80 my @tests_to_exclude = ();
81 my %valgrind_flagged_fcns = ();
82 my %valgrind_flagged_fcns_unique = ();
86 my $loopback_intf = '';
87 my $anonymize_results = 0;
88 my $current_test_file = "$output_dir/init";
89 my $tarfile = 'test_fwknop.tar.gz';
90 my $server_test_file = '';
92 my $valgrind_str = '';
93 my $enable_client_ip_resolve_test = 0;
94 my $saved_last_results = 0;
96 my $enable_recompilation_warnings_check = 0;
97 my $enable_make_distcheck = 0;
104 my $USE_PREDEF_PKTS = 1;
108 my $NEW_RULE_REQUIRED = 1;
109 my $REQUIRE_NO_NEW_RULE = 2;
110 my $NEW_RULE_REMOVED = 1;
111 my $REQUIRE_NO_NEW_REMOVED = 2;
119 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
123 exit 1 unless GetOptions(
124 'Anonymize-results' => \$anonymize_results,
125 'fwknop-path=s' => \$fwknopCmd,
126 'fwknopd-path=s' => \$fwknopdCmd,
127 'libfko-path=s' => \$libfko_bin,
128 'loopback-intf=s' => \$loopback_intf,
129 'test-include=s' => \$test_include,
130 'include=s' => \$test_include, ### synonym
131 'test-exclude=s' => \$test_exclude,
132 'exclude=s' => \$test_exclude, ### synonym
133 'enable-recompile-check' => \$enable_recompilation_warnings_check,
134 'enable-ip-resolve' => \$enable_client_ip_resolve_test,
135 'enable-distcheck' => \$enable_make_distcheck,
136 'List-mode' => \$list_mode,
137 'enable-valgrind' => \$use_valgrind,
138 'valgrind-path=s' => \$valgrindCmd,
139 'output-dir=s' => \$output_dir,
140 'diff' => \$diff_mode,
141 'diff-dir1=s' => \$diff_dir1,
142 'diff-dir2=s' => \$diff_dir2,
148 ### create an anonymized tar file of test suite results that can be
149 ### emailed around to assist in debugging fwknop communications
150 exit &anonymize_results() if $anonymize_results;
152 &identify_loopback_intf();
154 $valgrind_str = "$valgrindCmd --leak-check=full " .
155 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
157 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
159 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
160 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
161 "$local_key_file --verbose --verbose";
163 my $client_ip_resolve_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
164 "$fwknopCmd -A tcp/22 -R -D $loopback_ip --get-key " .
165 "$local_key_file --verbose --verbose";
167 my $default_client_gpg_args = "$default_client_args " .
168 "--gpg-recipient-key $gpg_server_key " .
169 "--gpg-signer-key $gpg_client_key " .
170 "--gpg-home-dir $gpg_client_home_dir";
172 my $default_client_gpg_args_no_homedir = "$default_client_args " .
173 "--gpg-recipient-key $gpg_server_key " .
174 "--gpg-signer-key $gpg_client_key ";
176 my $default_server_conf_args = "-c $cf{'def'} -a $cf{'def_access'} " .
177 "-d $default_digest_file -p $default_pid_file";
179 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
180 "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
181 "-a $cf{'gpg_access'} $intf_str " .
182 "-d $default_digest_file -p $default_pid_file";
184 my $default_server_gpg_args_no_pw = "LD_LIBRARY_PATH=$lib_dir " .
185 "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
186 "-a $cf{'gpg_no_pw_access'} $intf_str " .
187 "-d $default_digest_file -p $default_pid_file";
189 ### point the compiled binaries at the local libary path
190 ### instead of any installed libfko instance
191 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
193 ### main array that defines the tests we will run
196 'category' => 'recompilation',
197 'detail' => 'recompile and look for compilation warnings',
198 'err_msg' => 'compile warnings exist',
199 'function' => \&compile_warnings,
203 'category' => 'make distcheck',
204 'detail' => 'ensure proper distribution creation',
205 'err_msg' => 'could not create proper tarball',
206 'function' => \&make_distcheck,
210 'category' => 'build',
211 'subcategory' => 'client',
212 'detail' => 'binary exists',
213 'err_msg' => 'binary not found',
214 'function' => \&binary_exists,
215 'binary' => $fwknopCmd,
219 'category' => 'build security',
220 'subcategory' => 'client',
221 'detail' => 'Position Independent Executable (PIE)',
222 'err_msg' => 'non PIE binary (fwknop client)',
223 'function' => \&pie_binary,
224 'binary' => $fwknopCmd,
228 'category' => 'build security',
229 'subcategory' => 'client',
230 'detail' => 'stack protected binary',
231 'err_msg' => 'non stack protected binary (fwknop client)',
232 'function' => \&stack_protected_binary,
233 'binary' => $fwknopCmd,
237 'category' => 'build security',
238 'subcategory' => 'client',
239 'detail' => 'fortify source functions',
240 'err_msg' => 'source functions not fortified (fwknop client)',
241 'function' => \&fortify_source_functions,
242 'binary' => $fwknopCmd,
246 'category' => 'build security',
247 'subcategory' => 'client',
248 'detail' => 'read-only relocations',
249 'err_msg' => 'no read-only relocations (fwknop client)',
250 'function' => \&read_only_relocations,
251 'binary' => $fwknopCmd,
255 'category' => 'build security',
256 'subcategory' => 'client',
257 'detail' => 'immediate binding',
258 'err_msg' => 'no immediate binding (fwknop client)',
259 'function' => \&immediate_binding,
260 'binary' => $fwknopCmd,
265 'category' => 'build',
266 'subcategory' => 'server',
267 'detail' => 'binary exists',
268 'err_msg' => 'binary not found',
269 'function' => \&binary_exists,
270 'binary' => $fwknopdCmd,
275 'category' => 'build security',
276 'subcategory' => 'server',
277 'detail' => 'Position Independent Executable (PIE)',
278 'err_msg' => 'non PIE binary (fwknopd server)',
279 'function' => \&pie_binary,
280 'binary' => $fwknopdCmd,
284 'category' => 'build security',
285 'subcategory' => 'server',
286 'detail' => 'stack protected binary',
287 'err_msg' => 'non stack protected binary (fwknopd server)',
288 'function' => \&stack_protected_binary,
289 'binary' => $fwknopdCmd,
293 'category' => 'build security',
294 'subcategory' => 'server',
295 'detail' => 'fortify source functions',
296 'err_msg' => 'source functions not fortified (fwknopd server)',
297 'function' => \&fortify_source_functions,
298 'binary' => $fwknopdCmd,
302 'category' => 'build security',
303 'subcategory' => 'server',
304 'detail' => 'read-only relocations',
305 'err_msg' => 'no read-only relocations (fwknopd server)',
306 'function' => \&read_only_relocations,
307 'binary' => $fwknopdCmd,
311 'category' => 'build security',
312 'subcategory' => 'server',
313 'detail' => 'immediate binding',
314 'err_msg' => 'no immediate binding (fwknopd server)',
315 'function' => \&immediate_binding,
316 'binary' => $fwknopdCmd,
321 'category' => 'build',
322 'subcategory' => 'libfko',
323 'detail' => 'binary exists',
324 'err_msg' => 'binary not found',
325 'function' => \&binary_exists,
326 'binary' => $libfko_bin,
330 'category' => 'build security',
331 'subcategory' => 'libfko',
332 'detail' => 'stack protected binary',
333 'err_msg' => 'non stack protected binary (libfko)',
334 'function' => \&stack_protected_binary,
335 'binary' => $libfko_bin,
339 'category' => 'build security',
340 'subcategory' => 'libfko',
341 'detail' => 'fortify source functions',
342 'err_msg' => 'source functions not fortified (libfko)',
343 'function' => \&fortify_source_functions,
344 'binary' => $libfko_bin,
348 'category' => 'build security',
349 'subcategory' => 'libfko',
350 'detail' => 'read-only relocations',
351 'err_msg' => 'no read-only relocations (libfko)',
352 'function' => \&read_only_relocations,
353 'binary' => $libfko_bin,
357 'category' => 'build security',
358 'subcategory' => 'libfko',
359 'detail' => 'immediate binding',
360 'err_msg' => 'no immediate binding (libfko)',
361 'function' => \&immediate_binding,
362 'binary' => $libfko_bin,
367 'category' => 'preliminaries',
368 'subcategory' => 'client',
369 'detail' => 'usage info',
370 'err_msg' => 'could not get usage info',
371 'function' => \&generic_exec,
372 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
376 'category' => 'preliminaries',
377 'subcategory' => 'client',
378 'detail' => 'getopt() no such argument',
379 'err_msg' => 'getopt() allowed non-existant argument',
380 'function' => \&generic_exec,
381 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
386 'category' => 'preliminaries',
387 'subcategory' => 'client',
388 'detail' => '--test mode, packet not sent',
389 'err_msg' => '--test mode, packet sent?',
390 'function' => \&generic_exec,
391 'positive_output_matches' => [qr/test\smode\senabled/],
392 'cmdline' => "$default_client_args --test",
397 'category' => 'preliminaries',
398 'subcategory' => 'client',
399 'detail' => 'expected code version',
400 'err_msg' => 'code version mis-match',
401 'function' => \&expected_code_version,
402 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
407 'category' => 'preliminaries',
408 'subcategory' => 'server',
409 'detail' => 'usage info',
410 'err_msg' => 'could not get usage info',
411 'function' => \&generic_exec,
412 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
416 'category' => 'preliminaries',
417 'subcategory' => 'server',
418 'detail' => 'getopt() no such argument',
419 'err_msg' => 'getopt() allowed non-existant argument',
420 'function' => \&generic_exec,
421 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
427 'category' => 'preliminaries',
428 'subcategory' => 'server',
429 'detail' => 'expected code version',
430 'err_msg' => 'code version mis-match',
431 'function' => \&expected_code_version,
432 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
433 "$fwknopdCmd -c $cf{'def'} -a " .
434 "$cf{'def_access'} --version",
438 'category' => 'preliminaries',
439 'detail' => 'collecting system specifics',
440 'err_msg' => 'could not get complete system specs',
441 'function' => \&specs,
442 'binary' => $fwknopdCmd,
447 'category' => 'basic operations',
448 'detail' => 'dump config',
449 'err_msg' => 'could not dump configuration',
450 'function' => \&generic_exec,
451 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
453 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
454 "$fwknopdCmd -c $cf{'def'} " .
455 "-a $cf{'def_access'} --dump-config",
459 'category' => 'basic operations',
460 'detail' => 'override config',
461 'err_msg' => 'could not override configuration',
462 'function' => \&generic_exec,
463 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
465 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
466 "$fwknopdCmd $default_server_conf_args " .
467 "-O $conf_dir/override_fwknopd.conf --dump-config",
472 'category' => 'basic operations',
473 'subcategory' => 'client',
474 'detail' => '--get-key path validation',
475 'err_msg' => 'accepted improper --get-key path',
476 'function' => \&generic_exec,
477 'positive_output_matches' => [qr/could\snot\sopen/i],
479 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
480 "$fwknopCmd -A tcp/22 -s $fake_ip " .
481 "-D $loopback_ip --get-key not/there",
485 'category' => 'basic operations',
486 'subcategory' => 'client',
487 'detail' => 'require [-s|-R|-a]',
488 'err_msg' => 'allowed null allow IP',
489 'function' => \&generic_exec,
490 'positive_output_matches' => [qr/must\suse\sone\sof/i],
492 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
493 "$fwknopCmd -D $loopback_ip",
497 'category' => 'basic operations',
498 'subcategory' => 'client',
499 'detail' => '--allow-ip <IP> valid IP',
500 'err_msg' => 'permitted invalid --allow-ip arg',
501 'function' => \&generic_exec,
502 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
504 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
505 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
509 'category' => 'basic operations',
510 'subcategory' => 'client',
511 'detail' => '-A <proto>/<port> specification',
512 'err_msg' => 'permitted invalid -A <proto>/<port>',
513 'function' => \&generic_exec,
514 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
516 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
517 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
521 'category' => 'basic operations',
522 'subcategory' => 'client',
523 'detail' => 'generate SPA packet',
524 'err_msg' => 'could not generate SPA packet',
525 'function' => \&client_send_spa_packet,
526 'cmdline' => $default_client_args,
531 'category' => 'basic operations',
532 'subcategory' => 'server',
533 'detail' => 'list current fwknopd fw rules',
534 'err_msg' => 'could not list current fwknopd fw rules',
535 'function' => \&generic_exec,
536 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
537 "$fwknopdCmd $default_server_conf_args --fw-list",
541 'category' => 'basic operations',
542 'subcategory' => 'server',
543 'detail' => 'list all current fw rules',
544 'err_msg' => 'could not list all current fw rules',
545 'function' => \&generic_exec,
546 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
547 "$fwknopdCmd $default_server_conf_args --fw-list-all",
551 'category' => 'basic operations',
552 'subcategory' => 'server',
553 'detail' => 'flush current firewall rules',
554 'err_msg' => 'could not flush current fw rules',
555 'function' => \&generic_exec,
556 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
557 "$fwknopdCmd $default_server_conf_args --fw-flush",
562 'category' => 'basic operations',
563 'subcategory' => 'server',
565 'err_msg' => 'start error',
566 'function' => \&server_start,
567 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
568 "$fwknopdCmd $default_server_conf_args $intf_str",
572 'category' => 'basic operations',
573 'subcategory' => 'server',
575 'err_msg' => 'stop error',
576 'function' => \&server_stop,
577 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
578 "$fwknopdCmd $default_server_conf_args $intf_str",
582 'category' => 'basic operations',
583 'subcategory' => 'server',
584 'detail' => 'write PID',
585 'err_msg' => 'did not write PID',
586 'function' => \&write_pid,
587 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
588 "$fwknopdCmd $default_server_conf_args $intf_str",
593 'category' => 'basic operations',
594 'subcategory' => 'server',
595 'detail' => '--packet-limit 1 exit',
596 'err_msg' => 'did not exit after one packet',
597 'function' => \&server_packet_limit,
598 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
599 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
603 'category' => 'basic operations',
604 'subcategory' => 'server',
605 'detail' => 'ignore packets < min SPA len (140)',
606 'err_msg' => 'did not ignore small packets',
607 'function' => \&server_ignore_small_packets,
608 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
609 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
613 'category' => 'basic operations',
614 'subcategory' => 'server',
615 'detail' => '-P bpf filter ignore packet',
616 'err_msg' => 'filter did not ignore packet',
617 'function' => \&server_bpf_ignore_packet,
618 'cmdline' => $default_client_args,
619 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
620 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
621 qq|-P "udp port $non_std_spa_port"|,
626 'category' => 'Rijndael SPA',
627 'subcategory' => 'client+server',
628 'detail' => 'complete cycle (tcp/22 ssh)',
629 'err_msg' => 'could not complete SPA cycle',
630 'function' => \&spa_cycle,
631 'cmdline' => $default_client_args,
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,
639 'category' => 'Rijndael SPA',
640 'subcategory' => 'client+server',
641 'detail' => 'client IP resolve (tcp/22 ssh)',
642 'err_msg' => 'could not complete SPA cycle',
643 'function' => \&spa_cycle,
644 'cmdline' => $client_ip_resolve_args,
646 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
647 "$fwknopdCmd $default_server_conf_args $intf_str",
648 'fw_rule_created' => $NEW_RULE_REQUIRED,
649 'fw_rule_removed' => $NEW_RULE_REMOVED,
654 'category' => 'Rijndael SPA',
655 'subcategory' => 'client+server',
656 'detail' => 'dual usage access key (tcp/80 http)',
657 'err_msg' => 'could not complete SPA cycle',
658 'function' => \&spa_cycle,
659 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
660 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
661 "$local_key_file --verbose --verbose",
662 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
663 "$fwknopdCmd -c $cf{'def'} -a $cf{'dual_key_access'} " .
664 "-d $default_digest_file -p $default_pid_file $intf_str",
665 ### check for the first stanza that does not allow tcp/80 - the
666 ### second stanza allows this
667 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
668 'fw_rule_created' => $NEW_RULE_REQUIRED,
669 'fw_rule_removed' => $NEW_RULE_REMOVED,
673 'category' => 'Rijndael SPA',
674 'subcategory' => 'client+server',
675 'detail' => 'packet aging (past) (tcp/22 ssh)',
676 'err_msg' => 'old SPA packet accepted',
677 'function' => \&spa_cycle,
678 'cmdline' => "$default_client_args --time-offset-minus 300s",
679 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
680 "$fwknopdCmd $default_server_conf_args $intf_str",
681 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
682 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
686 'category' => 'Rijndael SPA',
687 'subcategory' => 'client+server',
688 'detail' => 'packet aging (future) (tcp/22 ssh)',
689 'err_msg' => 'future SPA packet accepted',
690 'function' => \&spa_cycle,
691 'cmdline' => "$default_client_args --time-offset-plus 300s",
692 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
693 "$fwknopdCmd $default_server_conf_args $intf_str",
694 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
695 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
699 'category' => 'Rijndael SPA',
700 'subcategory' => 'client+server',
701 'detail' => 'expired stanza (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{'exp_access'} " .
707 "-d $default_digest_file -p $default_pid_file $intf_str",
708 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
709 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
713 'category' => 'Rijndael SPA',
714 'subcategory' => 'client+server',
715 'detail' => 'invalid expire date (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{'invalid_exp_access'} " .
721 "-d $default_digest_file -p $default_pid_file $intf_str",
722 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
723 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
727 'category' => 'Rijndael SPA',
728 'subcategory' => 'client+server',
729 'detail' => 'expired epoch stanza (tcp/22 ssh)',
730 'err_msg' => 'SPA packet accepted',
731 'function' => \&spa_cycle,
732 'cmdline' => $default_client_args,
733 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
734 "$fwknopdCmd -c $cf{'def'} -a $cf{'exp_epoch_access'} " .
735 "-d $default_digest_file -p $default_pid_file $intf_str",
736 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
737 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
741 'category' => 'Rijndael SPA',
742 'subcategory' => 'client+server',
743 'detail' => 'future expired stanza (tcp/22 ssh)',
744 'err_msg' => 'SPA packet not accepted',
745 'function' => \&spa_cycle,
746 'cmdline' => $default_client_args,
747 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
748 "$fwknopdCmd -c $cf{'def'} -a $cf{'future_exp_access'} " .
749 "-d $default_digest_file -p $default_pid_file $intf_str",
750 'fw_rule_created' => $NEW_RULE_REQUIRED,
751 'fw_rule_removed' => $NEW_RULE_REMOVED,
756 'category' => 'Rijndael SPA',
757 'subcategory' => 'client+server',
758 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
759 'err_msg' => "improper OPEN_PORTS result",
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_access'} " .
764 "-d $default_digest_file -p $default_pid_file $intf_str",
765 'fw_rule_created' => $NEW_RULE_REQUIRED,
766 'fw_rule_removed' => $NEW_RULE_REMOVED,
770 'category' => 'Rijndael SPA',
771 'subcategory' => 'client+server',
772 'detail' => 'OPEN_PORTS mismatch',
773 'err_msg' => "SPA packet accepted",
774 'function' => \&spa_cycle,
775 'cmdline' => $default_client_args,
776 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
777 "$fwknopdCmd -c $cf{'def'} -a $cf{'open_ports_mismatch'} " .
778 "-d $default_digest_file -p $default_pid_file $intf_str",
779 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
780 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
784 'category' => 'Rijndael SPA',
785 'subcategory' => 'client+server',
786 'detail' => 'require user (tcp/22 ssh)',
787 'err_msg' => "missed require user criteria",
788 'function' => \&spa_cycle,
789 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
790 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
791 "$fwknopdCmd -c $cf{'def'} -a $cf{'require_user_access'} " .
792 "-d $default_digest_file -p $default_pid_file $intf_str",
793 'fw_rule_created' => $NEW_RULE_REQUIRED,
794 'fw_rule_removed' => $NEW_RULE_REMOVED,
798 'category' => 'Rijndael SPA',
799 'subcategory' => 'client+server',
800 'detail' => 'user mismatch (tcp/22 ssh)',
801 'err_msg' => "improper user accepted for access",
802 'function' => \&user_mismatch,
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{'user_mismatch_access'} " .
807 "-d $default_digest_file -p $default_pid_file $intf_str",
808 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
809 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
813 'category' => 'Rijndael SPA',
814 'subcategory' => 'client+server',
815 'detail' => 'require src (tcp/22 ssh)',
816 'err_msg' => "fw rule not created",
817 'function' => \&spa_cycle,
818 'cmdline' => $default_client_args,
819 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
820 "$fwknopdCmd -c $cf{'def'} -a $cf{'require_src_access'} " .
821 "-d $default_digest_file -p $default_pid_file $intf_str",
822 'fw_rule_created' => $NEW_RULE_REQUIRED,
823 'fw_rule_removed' => $NEW_RULE_REMOVED,
827 'category' => 'Rijndael SPA',
828 'subcategory' => 'client+server',
829 'detail' => 'mismatch require src (tcp/22 ssh)',
830 'err_msg' => "fw rule created",
831 'function' => \&spa_cycle,
832 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
833 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
834 "$local_key_file --verbose --verbose",
835 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
836 "$fwknopdCmd -c $cf{'def'} -a $cf{'require_src_access'} " .
837 "-d $default_digest_file -p $default_pid_file $intf_str",
838 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
839 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
844 'category' => 'Rijndael SPA',
845 'subcategory' => 'client+server',
846 'detail' => 'IP 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_src_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,
858 'category' => 'Rijndael SPA',
859 'subcategory' => 'client+server',
860 'detail' => '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_subnet_match'} " .
866 "-d $default_digest_file -p $default_pid_file $intf_str",
867 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
868 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
872 'category' => 'Rijndael SPA',
873 'subcategory' => 'client+server',
874 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
875 'err_msg' => "did not filter $loopback_ip",
876 'function' => \&spa_cycle,
877 'cmdline' => $default_client_args,
878 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
879 "$fwknopdCmd -c $cf{'def'} -a $cf{'no_multi_src'} " .
880 "-d $default_digest_file -p $default_pid_file $intf_str",
881 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
882 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
886 'category' => 'Rijndael SPA',
887 'subcategory' => 'client+server',
888 'detail' => 'IP 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{'ip_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,
900 'category' => 'Rijndael SPA',
901 'subcategory' => 'client+server',
902 'detail' => 'subnet 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{'subnet_src_match'} " .
908 "-d $default_digest_file -p $default_pid_file $intf_str",
909 'fw_rule_created' => $NEW_RULE_REQUIRED,
910 'fw_rule_removed' => $NEW_RULE_REMOVED,
914 'category' => 'Rijndael SPA',
915 'subcategory' => 'client+server',
916 'detail' => 'multi IP/net match (tcp/22 ssh)',
917 'err_msg' => "did not filter $loopback_ip",
918 'function' => \&spa_cycle,
919 'cmdline' => $default_client_args,
920 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
921 "$fwknopdCmd -c $cf{'def'} -a $cf{'multi_src_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,
928 'category' => 'Rijndael SPA',
929 'subcategory' => 'client+server',
930 'detail' => 'multi access 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{'multi_stanza_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,
942 'category' => 'Rijndael SPA',
943 'subcategory' => 'client+server',
944 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
945 'err_msg' => "could not complete SPA cycle",
946 'function' => \&spa_cycle,
947 'cmdline' => $default_client_args,
948 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
949 "$fwknopdCmd -c $cf{'def'} -a $cf{'broken_keys_access'} " .
950 "-d $default_digest_file -p $default_pid_file $intf_str",
951 'fw_rule_created' => $NEW_RULE_REQUIRED,
952 'fw_rule_removed' => $NEW_RULE_REMOVED,
957 'category' => 'Rijndael SPA',
958 'subcategory' => 'client+server',
959 'detail' => "non-enabled NAT (tcp/22 ssh)",
960 'err_msg' => "SPA packet not filtered",
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 $default_server_conf_args $intf_str",
965 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
966 'server_conf' => $cf{'nat'},
967 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
971 'category' => 'Rijndael SPA',
972 'subcategory' => 'client+server',
973 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
974 'err_msg' => "could not complete NAT SPA cycle",
975 'function' => \&spa_cycle,
976 'cmdline' => "$default_client_args -N $internal_nat_host:22",
977 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
978 "$fwknopdCmd -c $cf{'nat'} -a $cf{'open_ports_access'} " .
979 "-d $default_digest_file -p $default_pid_file $intf_str",
980 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
981 'fw_rule_created' => $NEW_RULE_REQUIRED,
982 'fw_rule_removed' => $NEW_RULE_REMOVED,
983 'server_conf' => $cf{'nat'},
987 'category' => 'Rijndael SPA',
988 'subcategory' => 'client+server',
989 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
990 'err_msg' => "could not complete NAT SPA cycle",
991 'function' => \&spa_cycle,
992 'cmdline' => $default_client_args,
993 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
994 "$fwknopdCmd -c $cf{'nat'} -a $cf{'force_nat_access'} " .
995 "-d $default_digest_file -p $default_pid_file $intf_str",
996 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
997 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
998 'fw_rule_created' => $NEW_RULE_REQUIRED,
999 'fw_rule_removed' => $NEW_RULE_REMOVED,
1000 'server_conf' => $cf{'nat'},
1004 'category' => 'Rijndael SPA',
1005 'subcategory' => 'client+server',
1006 'detail' => "local NAT $force_nat_host (tcp/22 ssh)",
1007 'err_msg' => "could not complete NAT SPA cycle",
1008 'function' => \&spa_cycle,
1009 'cmdline' => "$default_client_args --nat-local",
1010 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1011 "$fwknopdCmd -c $cf{'local_nat'} -a $cf{'force_nat_access'} " .
1012 "-d $default_digest_file -p $default_pid_file $intf_str",
1013 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i,
1014 qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
1015 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1016 'fw_rule_created' => $NEW_RULE_REQUIRED,
1017 'fw_rule_removed' => $NEW_RULE_REMOVED,
1018 'server_conf' => $cf{'nat'},
1022 'category' => 'Rijndael SPA',
1023 'subcategory' => 'client+server',
1024 'detail' => "local NAT non-FORCE_NAT (tcp/22 ssh)",
1025 'err_msg' => "could not complete NAT SPA cycle",
1026 'function' => \&spa_cycle,
1027 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1028 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
1029 "$local_key_file --verbose --verbose --nat-local --nat-port 22",
1030 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1031 "$fwknopdCmd -c $cf{'local_nat'} -a $cf{'def_access'} " .
1032 "-d $default_digest_file -p $default_pid_file $intf_str",
1033 'server_positive_output_matches' => [qr/to\:$loopback_ip\:22/i,
1034 qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
1035 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
1036 'fw_rule_created' => $NEW_RULE_REQUIRED,
1037 'fw_rule_removed' => $NEW_RULE_REMOVED,
1038 'server_conf' => $cf{'nat'},
1043 'category' => 'Rijndael SPA',
1044 'subcategory' => 'client+server',
1045 'detail' => 'complete cycle (tcp/23 telnet)',
1046 'err_msg' => 'could not complete SPA cycle',
1047 'function' => \&spa_cycle,
1048 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1049 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1050 "$local_key_file --verbose --verbose",
1051 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1052 "$fwknopdCmd $default_server_conf_args $intf_str",
1053 'fw_rule_created' => $NEW_RULE_REQUIRED,
1054 'fw_rule_removed' => $NEW_RULE_REMOVED,
1058 'category' => 'Rijndael SPA',
1059 'subcategory' => 'client+server',
1060 'detail' => 'complete cycle (tcp/9418 git)',
1061 'err_msg' => 'could not complete SPA cycle',
1062 'function' => \&spa_cycle,
1063 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1064 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1065 "$local_key_file --verbose --verbose",
1066 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1067 "$fwknopdCmd $default_server_conf_args $intf_str",
1068 'fw_rule_created' => $NEW_RULE_REQUIRED,
1069 'fw_rule_removed' => $NEW_RULE_REMOVED,
1073 'category' => 'Rijndael SPA',
1074 'subcategory' => 'client+server',
1075 'detail' => 'complete cycle (udp/53 dns)',
1076 'err_msg' => 'could not complete SPA cycle',
1077 'function' => \&spa_cycle,
1078 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1079 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1080 "$local_key_file --verbose --verbose",
1081 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1082 "$fwknopdCmd $default_server_conf_args $intf_str",
1083 'fw_rule_created' => $NEW_RULE_REQUIRED,
1084 'fw_rule_removed' => $NEW_RULE_REMOVED,
1088 'category' => 'Rijndael SPA',
1089 'subcategory' => 'client+server',
1090 'detail' => "-P bpf SPA over port $non_std_spa_port",
1091 'err_msg' => 'could not complete SPA cycle',
1092 'function' => \&spa_cycle,
1093 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1094 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1095 "$fwknopdCmd $default_server_conf_args $intf_str " .
1096 qq|-P "udp port $non_std_spa_port"|,
1097 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1098 'fw_rule_created' => $NEW_RULE_REQUIRED,
1099 'fw_rule_removed' => $NEW_RULE_REMOVED,
1104 'category' => 'Rijndael SPA',
1105 'subcategory' => 'client+server',
1106 'detail' => 'random SPA port (tcp/22 ssh)',
1107 'err_msg' => 'could not complete SPA cycle',
1108 'function' => \&spa_cycle,
1109 'cmdline' => "$default_client_args -r",
1110 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1111 "$fwknopdCmd $default_server_conf_args $intf_str " .
1113 'fw_rule_created' => $NEW_RULE_REQUIRED,
1114 'fw_rule_removed' => $NEW_RULE_REMOVED,
1119 'category' => 'Rijndael SPA',
1120 'subcategory' => 'client+server',
1121 'detail' => 'spoof username (tcp/22)',
1122 'err_msg' => 'could not spoof username',
1123 'function' => \&spoof_username,
1124 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1125 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1126 "$local_key_file --verbose --verbose",
1127 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1128 "$fwknopdCmd $default_server_conf_args $intf_str",
1133 'category' => 'Rijndael SPA',
1134 'subcategory' => 'client+server',
1135 'detail' => 'replay attack detection',
1136 'err_msg' => 'could not detect replay attack',
1137 'function' => \&replay_detection,
1138 'cmdline' => $default_client_args,
1139 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1140 "$fwknopdCmd $default_server_conf_args $intf_str",
1141 'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1145 'category' => 'Rijndael SPA',
1146 'subcategory' => 'client+server',
1147 'detail' => 'replay detection (Rijndael prefix)',
1148 'err_msg' => 'could not detect replay attack',
1149 'function' => \&replay_detection,
1150 'pkt_prefix' => 'U2FsdGVkX1',
1151 'cmdline' => $default_client_args,
1152 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1153 "$fwknopdCmd $default_server_conf_args $intf_str",
1154 'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1158 'category' => 'Rijndael SPA',
1159 'subcategory' => 'server',
1160 'detail' => 'digest cache structure',
1161 'err_msg' => 'improper digest cache structure',
1162 'function' => \&digest_cache_structure,
1167 'category' => 'Rijndael SPA',
1168 'subcategory' => 'server',
1169 'detail' => 'ipfw active/expire sets not equal',
1170 'err_msg' => 'allowed active/expire sets to be the same',
1171 'function' => \&spa_cycle,
1172 'cmdline' => $default_client_args,
1173 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1174 "$fwknopdCmd -c $cf{'ipfw_active_expire'} -a $cf{'def_access'} " .
1175 "-d $default_digest_file -p $default_pid_file $intf_str",
1176 'server_positive_output_matches' => [qr/Cannot\sset\sidentical\sipfw\sactive\sand\sexpire\ssets/],
1177 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
1182 'category' => 'Rijndael SPA',
1183 'subcategory' => 'client+server',
1184 'detail' => 'non-base64 altered SPA data',
1185 'err_msg' => 'allowed improper SPA data',
1186 'function' => \&altered_non_base64_spa_data,
1187 'cmdline' => $default_client_args,
1188 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1189 "$fwknopdCmd $default_server_conf_args $intf_str",
1193 'category' => 'Rijndael SPA',
1194 'subcategory' => 'client+server',
1195 'detail' => 'base64 altered SPA data',
1196 'err_msg' => 'allowed improper SPA data',
1197 'function' => \&altered_base64_spa_data,
1198 'cmdline' => $default_client_args,
1199 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1200 "$fwknopdCmd $default_server_conf_args $intf_str",
1204 'category' => 'Rijndael SPA',
1205 'subcategory' => 'client+server',
1206 'detail' => 'appended data to SPA pkt',
1207 'err_msg' => 'allowed improper SPA data',
1208 'function' => \&appended_spa_data,
1209 'cmdline' => $default_client_args,
1210 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1211 "$fwknopdCmd $default_server_conf_args $intf_str",
1215 'category' => 'Rijndael SPA',
1216 'subcategory' => 'client+server',
1217 'detail' => 'prepended data to SPA pkt',
1218 'err_msg' => 'allowed improper SPA data',
1219 'function' => \&prepended_spa_data,
1220 'cmdline' => $default_client_args,
1221 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1222 "$fwknopdCmd $default_server_conf_args $intf_str",
1227 'category' => 'GPG (no pw) SPA',
1228 'subcategory' => 'client+server',
1229 'detail' => 'complete cycle (tcp/22 ssh)',
1230 'err_msg' => 'could not complete SPA cycle',
1231 'function' => \&spa_cycle,
1232 'cmdline' => "$default_client_gpg_args_no_homedir "
1233 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1234 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1235 'fw_rule_created' => $NEW_RULE_REQUIRED,
1236 'fw_rule_removed' => $NEW_RULE_REMOVED,
1240 'category' => 'GPG (no pw) SPA',
1241 'subcategory' => 'client+server',
1242 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1243 'err_msg' => 'could not complete SPA cycle',
1244 'function' => \&spa_cycle,
1245 'cmdline' => "$default_client_gpg_args_no_homedir "
1246 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1247 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1248 "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
1249 "-a $cf{'multi_gpg_access'} $intf_str " .
1250 "-d $default_digest_file -p $default_pid_file",
1251 'fw_rule_created' => $NEW_RULE_REQUIRED,
1252 'fw_rule_removed' => $NEW_RULE_REMOVED,
1257 'category' => 'GPG (no pw) SPA',
1258 'subcategory' => 'client+server',
1259 'detail' => 'complete cycle (tcp/23 telnet)',
1260 'err_msg' => 'could not complete SPA cycle',
1261 'function' => \&spa_cycle,
1262 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1263 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1264 "$local_key_file --verbose --verbose " .
1265 "--gpg-recipient-key $gpg_server_key " .
1266 "--gpg-signer-key $gpg_client_key " .
1267 "--gpg-home-dir $gpg_client_home_dir_no_pw",
1268 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1269 'fw_rule_created' => $NEW_RULE_REQUIRED,
1270 'fw_rule_removed' => $NEW_RULE_REMOVED,
1274 'category' => 'GPG (no pw) SPA',
1275 'subcategory' => 'client+server',
1276 'detail' => 'complete cycle (tcp/9418 git)',
1277 'err_msg' => 'could not complete SPA cycle',
1278 'function' => \&spa_cycle,
1279 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1280 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1281 "$local_key_file --verbose --verbose " .
1282 "--gpg-recipient-key $gpg_server_key " .
1283 "--gpg-signer-key $gpg_client_key " .
1284 "--gpg-home-dir $gpg_client_home_dir_no_pw",
1285 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1286 'fw_rule_created' => $NEW_RULE_REQUIRED,
1287 'fw_rule_removed' => $NEW_RULE_REMOVED,
1291 'category' => 'GPG (no pw) SPA',
1292 'subcategory' => 'client+server',
1293 'detail' => 'complete cycle (udp/53 dns)',
1294 'err_msg' => 'could not complete SPA cycle',
1295 'function' => \&spa_cycle,
1296 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1297 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1298 "$local_key_file --verbose --verbose " .
1299 "--gpg-recipient-key $gpg_server_key " .
1300 "--gpg-signer-key $gpg_client_key " .
1301 "--gpg-home-dir $gpg_client_home_dir_no_pw",
1302 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1303 'fw_rule_created' => $NEW_RULE_REQUIRED,
1304 'fw_rule_removed' => $NEW_RULE_REMOVED,
1309 'category' => 'GPG (no pw) SPA',
1310 'subcategory' => 'client+server',
1311 'detail' => 'replay attack detection',
1312 'err_msg' => 'could not detect replay attack',
1313 'function' => \&replay_detection,
1314 'cmdline' => "$default_client_gpg_args_no_homedir "
1315 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1316 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1317 'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1321 'category' => 'GPG (no pw) SPA',
1322 'subcategory' => 'client+server',
1323 'detail' => 'replay detection (GnuPG prefix)',
1324 'err_msg' => 'could not detect replay attack',
1325 'function' => \&replay_detection,
1326 'pkt_prefix' => 'hQ',
1327 'cmdline' => "$default_client_gpg_args_no_homedir "
1328 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1329 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1330 "$fwknopdCmd $default_server_conf_args $intf_str",
1331 'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1336 'category' => 'GPG (no pw) SPA',
1337 'subcategory' => 'client+server',
1338 'detail' => 'non-base64 altered SPA data',
1339 'err_msg' => 'allowed improper SPA data',
1340 'function' => \&altered_non_base64_spa_data,
1341 'cmdline' => "$default_client_gpg_args_no_homedir "
1342 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1343 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1347 'category' => 'GPG (no pw) SPA',
1348 'subcategory' => 'client+server',
1349 'detail' => 'base64 altered SPA data',
1350 'err_msg' => 'allowed improper SPA data',
1351 'function' => \&altered_base64_spa_data,
1352 'cmdline' => "$default_client_gpg_args_no_homedir "
1353 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1354 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1358 'category' => 'GPG (no pw) SPA',
1359 'subcategory' => 'client+server',
1360 'detail' => 'appended data to SPA pkt',
1361 'err_msg' => 'allowed improper SPA data',
1362 'function' => \&appended_spa_data,
1363 'cmdline' => "$default_client_gpg_args_no_homedir "
1364 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1365 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1369 'category' => 'GPG (no pw) SPA',
1370 'subcategory' => 'client+server',
1371 'detail' => 'prepended data to SPA pkt',
1372 'err_msg' => 'allowed improper SPA data',
1373 'function' => \&prepended_spa_data,
1374 'cmdline' => "$default_client_gpg_args_no_homedir "
1375 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1376 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1380 'category' => 'GPG (no pw) SPA',
1381 'subcategory' => 'client+server',
1382 'detail' => 'spoof username (tcp/22 ssh)',
1383 'err_msg' => 'could not spoof username',
1384 'function' => \&spoof_username,
1385 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args_no_homedir "
1386 . "--gpg-home-dir $gpg_client_home_dir_no_pw",
1387 'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
1392 'category' => 'GnuPG (GPG) SPA',
1393 'subcategory' => 'client+server',
1394 'detail' => 'complete cycle (tcp/22 ssh)',
1395 'err_msg' => 'could not complete SPA cycle',
1396 'function' => \&spa_cycle,
1397 'cmdline' => $default_client_gpg_args,
1398 'fwknopd_cmdline' => $default_server_gpg_args,
1399 'fw_rule_created' => $NEW_RULE_REQUIRED,
1400 'fw_rule_removed' => $NEW_RULE_REMOVED,
1404 'category' => 'GnuPG (GPG) SPA',
1405 'subcategory' => 'client+server',
1406 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1407 'err_msg' => 'could not complete SPA cycle',
1408 'function' => \&spa_cycle,
1409 'cmdline' => $default_client_gpg_args,
1410 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1411 "$valgrind_str $fwknopdCmd -c $cf{'def'} " .
1412 "-a $cf{'multi_gpg_access'} $intf_str " .
1413 "-d $default_digest_file -p $default_pid_file",
1414 'fw_rule_created' => $NEW_RULE_REQUIRED,
1415 'fw_rule_removed' => $NEW_RULE_REMOVED,
1420 'category' => 'GnuPG (GPG) SPA',
1421 'subcategory' => 'client+server',
1422 'detail' => 'complete cycle (tcp/23 telnet)',
1423 'err_msg' => 'could not complete SPA cycle',
1424 'function' => \&spa_cycle,
1425 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1426 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1427 "$local_key_file --verbose --verbose " .
1428 "--gpg-recipient-key $gpg_server_key " .
1429 "--gpg-signer-key $gpg_client_key " .
1430 "--gpg-home-dir $gpg_client_home_dir",
1431 'fwknopd_cmdline' => $default_server_gpg_args,
1432 'fw_rule_created' => $NEW_RULE_REQUIRED,
1433 'fw_rule_removed' => $NEW_RULE_REMOVED,
1437 'category' => 'GnuPG (GPG) SPA',
1438 'subcategory' => 'client+server',
1439 'detail' => 'complete cycle (tcp/9418 git)',
1440 'err_msg' => 'could not complete SPA cycle',
1441 'function' => \&spa_cycle,
1442 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1443 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1444 "$local_key_file --verbose --verbose " .
1445 "--gpg-recipient-key $gpg_server_key " .
1446 "--gpg-signer-key $gpg_client_key " .
1447 "--gpg-home-dir $gpg_client_home_dir",
1448 'fwknopd_cmdline' => $default_server_gpg_args,
1449 'fw_rule_created' => $NEW_RULE_REQUIRED,
1450 'fw_rule_removed' => $NEW_RULE_REMOVED,
1454 'category' => 'GnuPG (GPG) SPA',
1455 'subcategory' => 'client+server',
1456 'detail' => 'complete cycle (udp/53 dns)',
1457 'err_msg' => 'could not complete SPA cycle',
1458 'function' => \&spa_cycle,
1459 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1460 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1461 "$local_key_file --verbose --verbose " .
1462 "--gpg-recipient-key $gpg_server_key " .
1463 "--gpg-signer-key $gpg_client_key " .
1464 "--gpg-home-dir $gpg_client_home_dir",
1465 'fwknopd_cmdline' => $default_server_gpg_args,
1466 'fw_rule_created' => $NEW_RULE_REQUIRED,
1467 'fw_rule_removed' => $NEW_RULE_REMOVED,
1472 'category' => 'GnuPG (GPG) SPA',
1473 'subcategory' => 'client+server',
1474 'detail' => 'replay attack detection',
1475 'err_msg' => 'could not detect replay attack',
1476 'function' => \&replay_detection,
1477 'cmdline' => $default_client_gpg_args,
1478 'fwknopd_cmdline' => $default_server_gpg_args,
1479 'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/],
1483 'category' => 'GnuPG (GPG) SPA',
1484 'subcategory' => 'client+server',
1485 'detail' => 'replay detection (GnuPG prefix)',
1486 'err_msg' => 'could not detect replay attack',
1487 'function' => \&replay_detection,
1488 'pkt_prefix' => 'hQ',
1489 'cmdline' => $default_client_gpg_args,
1490 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1491 "$fwknopdCmd $default_server_conf_args $intf_str",
1492 'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
1497 'category' => 'GnuPG (GPG) SPA',
1498 'subcategory' => 'client+server',
1499 'detail' => 'non-base64 altered SPA data',
1500 'err_msg' => 'allowed improper SPA data',
1501 'function' => \&altered_non_base64_spa_data,
1502 'cmdline' => $default_client_gpg_args,
1503 'fwknopd_cmdline' => $default_server_gpg_args,
1507 'category' => 'GnuPG (GPG) SPA',
1508 'subcategory' => 'client+server',
1509 'detail' => 'base64 altered SPA data',
1510 'err_msg' => 'allowed improper SPA data',
1511 'function' => \&altered_base64_spa_data,
1512 'cmdline' => $default_client_gpg_args,
1513 'fwknopd_cmdline' => $default_server_gpg_args,
1517 'category' => 'GnuPG (GPG) SPA',
1518 'subcategory' => 'client+server',
1519 'detail' => 'appended data to SPA pkt',
1520 'err_msg' => 'allowed improper SPA data',
1521 'function' => \&appended_spa_data,
1522 'cmdline' => $default_client_gpg_args,
1523 'fwknopd_cmdline' => $default_server_gpg_args,
1527 'category' => 'GnuPG (GPG) SPA',
1528 'subcategory' => 'client+server',
1529 'detail' => 'prepended data to SPA pkt',
1530 'err_msg' => 'allowed improper SPA data',
1531 'function' => \&prepended_spa_data,
1532 'cmdline' => $default_client_gpg_args,
1533 'fwknopd_cmdline' => $default_server_gpg_args,
1537 'category' => 'GnuPG (GPG) SPA',
1538 'subcategory' => 'client+server',
1539 'detail' => 'spoof username (tcp/22 ssh)',
1540 'err_msg' => 'could not spoof username',
1541 'function' => \&spoof_username,
1542 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1543 'fwknopd_cmdline' => $default_server_gpg_args,
1547 'category' => 'GnuPG (GPG) SPA',
1548 'subcategory' => 'server',
1549 'detail' => 'digest cache structure',
1550 'err_msg' => 'improper digest cache structure',
1551 'function' => \&digest_cache_structure,
1556 if ($use_valgrind) {
1559 'category' => 'valgrind output',
1560 'subcategory' => 'flagged functions',
1562 'err_msg' => 'could not parse flagged functions',
1563 'function' => \&parse_valgrind_flagged_functions,
1569 'category' => $REQUIRED,
1570 'subcategory' => $OPTIONAL,
1571 'detail' => $REQUIRED,
1572 'function' => $REQUIRED,
1573 'binary' => $OPTIONAL,
1574 'cmdline' => $OPTIONAL,
1575 'fwknopd_cmdline' => $OPTIONAL,
1576 'fatal' => $OPTIONAL,
1577 'exec_err' => $OPTIONAL,
1578 'fw_rule_created' => $OPTIONAL,
1579 'fw_rule_removed' => $OPTIONAL,
1580 'server_conf' => $OPTIONAL,
1581 'pkt_prefix' => $OPTIONAL,
1582 'no_ip_check' => $OPTIONAL,
1583 'positive_output_matches' => $OPTIONAL,
1584 'negative_output_matches' => $OPTIONAL,
1585 'server_positive_output_matches' => $OPTIONAL,
1586 'server_negative_output_matches' => $OPTIONAL,
1587 'replay_positive_output_matches' => $OPTIONAL,
1588 'replay_negative_output_matches' => $OPTIONAL,
1592 &diff_test_results();
1596 ### make sure everything looks as expected before continuing
1599 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1600 " args: @args_cp\n\n"
1603 ### save the results from any previous test suite run
1604 ### so that we can potentially compare them with --diff
1605 if ($saved_last_results) {
1606 &logr(" Saved results from previous run " .
1607 "to: ${output_dir}.last/\n\n");
1610 ### main loop through all of the tests
1611 for my $test_hr (@tests) {
1612 &run_test($test_hr);
1615 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1617 copy $logfile, "$output_dir/$logfile" or die $!;
1621 #===================== end main =======================
1624 my $test_hr = shift;
1626 my $msg = "[$test_hr->{'category'}]";
1627 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1628 $msg .= " $test_hr->{'detail'}";
1630 return unless &process_include_exclude($msg);
1640 $current_test_file = "$output_dir/$executed.test";
1641 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1643 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1644 $test_hr->{'msg'} = $msg;
1645 if (&{$test_hr->{'function'}}($test_hr)) {
1646 &logr("pass ($executed)\n");
1649 &logr("fail ($executed)\n");
1652 if ($test_hr->{'fatal'} eq $YES) {
1653 die "[*] required test failed, exiting.";
1660 sub process_include_exclude() {
1663 ### inclusions/exclusions
1664 if (@tests_to_include) {
1666 for my $test (@tests_to_include) {
1667 if ($msg =~ $test or ($use_valgrind
1668 and $msg =~ /valgrind\soutput/)) {
1673 return 0 unless $found;
1675 if (@tests_to_exclude) {
1677 for my $test (@tests_to_exclude) {
1678 if ($msg =~ $test) {
1688 sub diff_test_results() {
1690 $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
1691 $diff_dir2 = $output_dir unless $diff_dir2;
1693 die "[*] Need results from a previous run before running --diff"
1694 unless -d $diff_dir2;
1695 die "[*] Current results set does not exist." unless -d $diff_dir1;
1697 my %current_tests = ();
1698 my %previous_tests = ();
1700 ### Only diff results for matching tests (parse the logfile to see which
1701 ### test numbers match across the two test cycles).
1702 &build_results_hash(\%current_tests, $diff_dir1);
1703 &build_results_hash(\%previous_tests, $diff_dir2);
1705 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1706 keys %current_tests) {
1707 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1708 my $current_num = $current_tests{$test_msg}{'num'};
1709 if (defined $previous_tests{$test_msg}) {
1710 print "[+] Checking: $test_msg\n";
1711 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1712 my $previous_num = $previous_tests{$test_msg}{'num'};
1713 if ($current_result ne $previous_result) {
1714 print " DIFF: **$current_result** $test_msg\n";
1717 &diff_results($previous_num, $current_num);
1725 sub diff_results() {
1726 my ($previous_num, $current_num) = @_;
1728 ### edit out any valgrind "==354==" prefixes
1729 my $valgrind_search_re = qr/^==\d+==\s/;
1731 ### remove CMD timestamps
1732 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1734 for my $file ("$diff_dir1/${previous_num}.test",
1735 "$diff_dir1/${previous_num}_fwknopd.test",
1736 "$diff_dir2/${current_num}.test",
1737 "$diff_dir2/${current_num}_fwknopd.test",
1739 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1740 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1743 if (-e "$diff_dir1/${previous_num}.test"
1744 and -e "$diff_dir2/${current_num}.test") {
1745 system "diff -u $diff_dir1/${previous_num}.test " .
1746 "$diff_dir2/${current_num}.test";
1749 if (-e "$diff_dir1/${previous_num}_fwknopd.test"
1750 and -e "$diff_dir2/${current_num}_fwknopd.test") {
1751 system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
1752 "$diff_dir2/${current_num}_fwknopd.test";
1758 sub build_results_hash() {
1759 my ($hr, $dir) = @_;
1761 open F, "< $dir/$logfile" or die $!;
1763 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1764 $hr->{$1}{'pass_fail'} = $2;
1765 $hr->{$1}{'num'} = $3;
1771 sub compile_warnings() {
1773 ### 'make clean' as root
1774 return 0 unless &run_cmd('make -C .. clean',
1775 $cmd_out_tmp, $current_test_file);
1778 my $username = getpwuid((stat($configure_path))[4]);
1779 die "[*] Could not determine $configure_path owner"
1782 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1783 $cmd_out_tmp, $current_test_file);
1787 return 0 unless &run_cmd('make -C ..',
1788 $cmd_out_tmp, $current_test_file);
1792 ### look for compilation warnings - something like:
1793 ### warning: ‘test’ is used uninitialized in this function
1794 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
1795 $MATCH_ANY, $current_test_file);
1797 ### the new binaries should exist
1798 unless (-e $fwknopCmd and -x $fwknopCmd) {
1799 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1800 $current_test_file);
1802 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1803 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1804 $current_test_file);
1810 sub make_distcheck() {
1812 ### 'make clean' as root
1813 return 0 unless &run_cmd('make -C .. distcheck',
1814 $cmd_out_tmp, $current_test_file);
1816 ### look for compilation warnings - something like:
1817 ### warning: ‘test’ is used uninitialized in this function
1818 return 1 if &file_find_regex([qr/archives\sready\sfor\sdistribution/],
1819 $MATCH_ALL, $current_test_file);
1825 sub binary_exists() {
1826 my $test_hr = shift;
1827 return 0 unless $test_hr->{'binary'};
1829 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1830 ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X)
1832 if ($test_hr->{'binary'} =~ /libfko/) {
1833 unless (-e $test_hr->{'binary'}) {
1834 my $file = "$lib_dir/libfko.dylib";
1836 $test_hr->{'binary'} = $file;
1837 $libfko_bin = $file;
1839 for my $f (glob("$lib_dir/libfko.so*")) {
1840 if (-e $f and -x $f) {
1841 $test_hr->{'binary'} = $f;
1850 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1854 sub expected_code_version() {
1855 my $test_hr = shift;
1857 unless (-e '../VERSION') {
1858 &write_test_file("[-] ../VERSION file does not exist.\n",
1859 $current_test_file);
1863 open F, '< ../VERSION' or die $!;
1866 if ($line =~ /(\d.*\d)/) {
1868 return 0 unless &run_cmd($test_hr->{'cmdline'},
1869 $cmd_out_tmp, $current_test_file);
1870 return 1 if &file_find_regex([qr/$version/],
1871 $MATCH_ALL, $current_test_file);
1876 sub client_send_spa_packet() {
1877 my $test_hr = shift;
1879 &write_key('fwknoptest', $local_key_file);
1881 return 0 unless &run_cmd($test_hr->{'cmdline'},
1882 $cmd_out_tmp, $current_test_file);
1883 return 0 unless &file_find_regex([qr/final\spacked/i],
1884 $MATCH_ALL, $current_test_file);
1890 my $test_hr = shift;
1892 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1893 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1895 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1896 $rv = 0 unless $fw_rule_created;
1897 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1898 $rv = 0 if $fw_rule_created;
1901 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1902 $rv = 0 unless $fw_rule_removed;
1903 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1904 $rv = 0 if $fw_rule_removed;
1907 if ($test_hr->{'server_positive_output_matches'}) {
1908 $rv = 0 unless &file_find_regex(
1909 $test_hr->{'server_positive_output_matches'},
1910 $MATCH_ALL, $server_test_file);
1913 if ($test_hr->{'server_negative_output_matches'}) {
1914 $rv = 0 if &file_find_regex(
1915 $test_hr->{'server_negative_output_matches'},
1916 $MATCH_ANY, $server_test_file);
1922 sub spoof_username() {
1923 my $test_hr = shift;
1925 my $rv = &spa_cycle($test_hr);
1927 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1928 $MATCH_ALL, $current_test_file)) {
1932 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1933 $MATCH_ALL, $server_test_file)) {
1940 sub replay_detection() {
1941 my $test_hr = shift;
1943 ### do a complete SPA cycle and then parse the SPA packet out of the
1944 ### current test file and re-send
1946 return 0 unless &spa_cycle($test_hr);
1948 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1951 &write_test_file("[-] could not get SPA packet " .
1952 "from file: $current_test_file\n",
1953 $current_test_file);
1957 if ($test_hr->{'pkt_prefix'}) {
1958 $spa_pkt = $test_hr->{'pkt_prefix'} . $spa_pkt;
1964 'port' => $default_spa_port,
1965 'dst_ip' => $loopback_ip,
1970 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1971 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1973 $rv = 0 unless $server_was_stopped;
1975 if ($test_hr->{'replay_positive_output_matches'}) {
1976 $rv = 0 unless &file_find_regex(
1977 $test_hr->{'replay_positive_output_matches'},
1978 $MATCH_ALL, $server_test_file);
1981 if ($test_hr->{'replay_negative_output_matches'}) {
1982 $rv = 0 if &file_find_regex(
1983 $test_hr->{'replay_negative_output_matches'},
1984 $MATCH_ANY, $server_test_file);
1990 sub digest_cache_structure() {
1991 my $test_hr = shift;
1994 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1996 if (&file_find_regex([qr/ASCII/i], $MATCH_ALL, $cmd_out_tmp)) {
1998 ### the format should be:
1999 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
2000 open F, "< $default_digest_file" or
2001 die "[*] could not open $default_digest_file: $!";
2005 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
2006 &write_test_file("[-] invalid digest.cache line: $_",
2007 $current_test_file);
2013 } elsif (&file_find_regex([qr/dbm/i], $MATCH_ALL, $cmd_out_tmp)) {
2014 &write_test_file("[+] DBM digest file format, " .
2015 "assuming this is valid.\n", $current_test_file);
2017 ### don't know what kind of file the digest.cache is
2018 &write_test_file("[-] unrecognized file type for " .
2019 "$default_digest_file.\n", $current_test_file);
2024 &write_test_file("[+] valid digest.cache structure.\n",
2025 $current_test_file);
2031 sub server_bpf_ignore_packet() {
2032 my $test_hr = shift;
2035 my $server_was_stopped = 0;
2036 my $fw_rule_created = 0;
2037 my $fw_rule_removed = 0;
2039 unless (&client_send_spa_packet($test_hr)) {
2040 &write_test_file("[-] fwknop client execution error.\n",
2041 $current_test_file);
2045 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2048 &write_test_file("[-] could not get SPA packet " .
2049 "from file: $current_test_file\n", $current_test_file);
2056 'port' => $default_spa_port,
2057 'dst_ip' => $loopback_ip,
2062 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2063 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2065 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
2066 $MATCH_ALL, $server_test_file)) {
2073 sub altered_non_base64_spa_data() {
2074 my $test_hr = shift;
2077 my $server_was_stopped = 0;
2078 my $fw_rule_created = 0;
2079 my $fw_rule_removed = 0;
2081 unless (&client_send_spa_packet($test_hr)) {
2082 &write_test_file("[-] fwknop client execution error.\n",
2083 $current_test_file);
2087 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2090 &write_test_file("[-] could not get SPA packet " .
2091 "from file: $current_test_file\n", $current_test_file);
2095 ### alter one byte (change to a ":")
2096 $spa_pkt =~ s|^(.{3}).|$1:|;
2101 'port' => $default_spa_port,
2102 'dst_ip' => $loopback_ip,
2107 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2108 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2110 $rv = 0 unless $server_was_stopped;
2115 sub altered_base64_spa_data() {
2116 my $test_hr = shift;
2119 my $server_was_stopped = 0;
2120 my $fw_rule_created = 0;
2121 my $fw_rule_removed = 0;
2123 unless (&client_send_spa_packet($test_hr)) {
2124 &write_test_file("[-] fwknop client execution error.\n",
2125 $current_test_file);
2129 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2132 &write_test_file("[-] could not get SPA packet " .
2133 "from file: $current_test_file\n", $current_test_file);
2137 $spa_pkt =~ s|^(.{3}).|AAAA|;
2142 'port' => $default_spa_port,
2143 'dst_ip' => $loopback_ip,
2148 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2149 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2151 $rv = 0 unless $server_was_stopped;
2153 if ($fw_rule_created) {
2154 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2157 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2160 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2161 $MATCH_ALL, $server_test_file)) {
2168 sub appended_spa_data() {
2169 my $test_hr = shift;
2172 my $server_was_stopped = 0;
2173 my $fw_rule_created = 0;
2174 my $fw_rule_removed = 0;
2176 unless (&client_send_spa_packet($test_hr)) {
2177 &write_test_file("[-] fwknop client execution error.\n",
2178 $current_test_file);
2182 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2185 &write_test_file("[-] could not get SPA packet " .
2186 "from file: $current_test_file\n", $current_test_file);
2195 'port' => $default_spa_port,
2196 'dst_ip' => $loopback_ip,
2201 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2202 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2204 $rv = 0 unless $server_was_stopped;
2206 if ($fw_rule_created) {
2207 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2210 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2213 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2214 $MATCH_ALL, $server_test_file)) {
2221 sub prepended_spa_data() {
2222 my $test_hr = shift;
2225 my $server_was_stopped = 0;
2226 my $fw_rule_created = 0;
2227 my $fw_rule_removed = 0;
2229 unless (&client_send_spa_packet($test_hr)) {
2230 &write_test_file("[-] fwknop client execution error.\n",
2231 $current_test_file);
2235 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2238 &write_test_file("[-] could not get SPA packet " .
2239 "from file: $current_test_file\n", $current_test_file);
2243 $spa_pkt = 'AAAA' . $spa_pkt;
2248 'port' => $default_spa_port,
2249 'dst_ip' => $loopback_ip,
2254 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2255 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2257 $rv = 0 unless $server_was_stopped;
2259 if ($fw_rule_created) {
2260 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2263 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2266 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2267 $MATCH_ALL, $server_test_file)) {
2274 sub server_start() {
2275 my $test_hr = shift;
2277 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2278 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2280 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
2281 $MATCH_ALL, $server_test_file)) {
2285 $rv = 0 unless $server_was_stopped;
2291 my $test_hr = shift;
2293 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2294 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2296 $rv = 0 unless $server_was_stopped;
2301 sub server_packet_limit() {
2302 my $test_hr = shift;
2307 'port' => $default_spa_port,
2308 'dst_ip' => $loopback_ip,
2313 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2314 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2316 if (&is_fwknopd_running()) {
2321 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2322 $MATCH_ALL, $server_test_file)) {
2326 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2327 $MATCH_ALL, $server_test_file)) {
2334 sub server_ignore_small_packets() {
2335 my $test_hr = shift;
2340 'port' => $default_spa_port,
2341 'dst_ip' => $loopback_ip,
2342 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2346 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2347 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2351 if (&is_fwknopd_running()) {
2359 sub client_server_interaction() {
2360 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2363 my $server_was_stopped = 1;
2364 my $fw_rule_created = 1;
2365 my $fw_rule_removed = 0;
2367 ### start fwknopd to monitor for the SPA packet over the loopback interface
2368 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2370 ### give fwknopd a chance to parse its config and start sniffing
2371 ### on the loopback interface
2372 if ($use_valgrind) {
2378 ### send the SPA packet(s) to the server either manually using IO::Socket or
2379 ### with the fwknopd client
2380 if ($spa_client_flag == $USE_CLIENT) {
2381 unless (&client_send_spa_packet($test_hr)) {
2382 &write_test_file("[-] fwknop client execution error.\n",
2383 $current_test_file);
2387 &send_packets($pkts_hr);
2390 ### check to see if the SPA packet resulted in a new fw access rule
2392 while (not &is_fw_rule_active($test_hr)) {
2393 &write_test_file("[-] new fw rule does not exist.\n",
2394 $current_test_file);
2400 $fw_rule_created = 0;
2401 $fw_rule_removed = 0;
2404 &time_for_valgrind() if $use_valgrind;
2406 if ($fw_rule_created) {
2407 sleep 3; ### allow time for rule time out.
2408 if (&is_fw_rule_active($test_hr)) {
2409 &write_test_file("[-] new fw rule not timed out.\n",
2410 $current_test_file);
2413 &write_test_file("[+] new fw rule timed out.\n",
2414 $current_test_file);
2415 $fw_rule_removed = 1;
2419 if (&is_fwknopd_running()) {
2421 unless (&file_find_regex([qr/Got\sSIGTERM/],
2422 $MATCH_ALL, $server_test_file)) {
2423 $server_was_stopped = 0;
2426 &write_test_file("[-] server is not running.\n",
2427 $current_test_file);
2428 $server_was_stopped = 0;
2431 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2434 sub get_spa_packet_from_file() {
2439 my $found_trigger_line = 0;
2440 open F, "< $file" or die "[*] Could not open file $file: $!";
2442 if (/final\spacked/i) {
2443 $found_trigger_line = 1;
2446 next unless $found_trigger_line;
2448 ### the next line with non whitespace is the SPA packet
2459 sub send_packets() {
2460 my $pkts_ar = shift;
2462 open F, ">> $current_test_file" or die $!;
2463 print F "[+] send_packets(): Sending the following packets...\n";
2464 print F Dumper $pkts_ar;
2467 for my $pkt_hr (@$pkts_ar) {
2468 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2469 my $socket = IO::Socket::INET->new(
2470 PeerAddr => $pkt_hr->{'dst_ip'},
2471 PeerPort => $pkt_hr->{'port'},
2472 Proto => $pkt_hr->{'proto'},
2474 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2475 "socket to $pkt_hr->{'dst_ip'}: $!";
2477 $socket->send($pkt_hr->{'data'});
2480 } elsif ($pkt_hr->{'proto'} eq 'http') {
2482 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2486 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2491 sub generic_exec() {
2492 my $test_hr = shift;
2496 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2497 $cmd_out_tmp, $current_test_file);
2499 if ($test_hr->{'exec_err'} eq $YES) {
2500 $rv = 0 if $exec_rv;
2502 $rv = 0 unless $exec_rv;
2505 if ($test_hr->{'positive_output_matches'}) {
2506 $rv = 0 unless &file_find_regex(
2507 $test_hr->{'positive_output_matches'},
2508 $MATCH_ALL, $current_test_file);
2511 if ($test_hr->{'negative_output_matches'}) {
2512 $rv = 0 if &file_find_regex(
2513 $test_hr->{'negative_output_matches'},
2514 $MATCH_ANY, $current_test_file);
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/Position\sIndependent.*:\sno/i],
2527 $MATCH_ALL, $current_test_file);
2531 ### check for stack protection
2532 sub stack_protected_binary() {
2533 my $test_hr = shift;
2534 return 0 unless $test_hr->{'binary'};
2535 &run_cmd("./hardening-check $test_hr->{'binary'}",
2536 $cmd_out_tmp, $current_test_file);
2537 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2538 $MATCH_ALL, $current_test_file);
2542 ### check for fortified source functions
2543 sub fortify_source_functions() {
2544 my $test_hr = shift;
2545 return 0 unless $test_hr->{'binary'};
2546 &run_cmd("./hardening-check $test_hr->{'binary'}",
2547 $cmd_out_tmp, $current_test_file);
2548 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2549 $MATCH_ALL, $current_test_file);
2553 ### check for read-only relocations
2554 sub read_only_relocations() {
2555 my $test_hr = shift;
2556 return 0 unless $test_hr->{'binary'};
2557 &run_cmd("./hardening-check $test_hr->{'binary'}",
2558 $cmd_out_tmp, $current_test_file);
2559 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2560 $MATCH_ALL, $current_test_file);
2564 ### check for immediate binding
2565 sub immediate_binding() {
2566 my $test_hr = shift;
2567 return 0 unless $test_hr->{'binary'};
2568 &run_cmd("./hardening-check $test_hr->{'binary'}",
2569 $cmd_out_tmp, $current_test_file);
2570 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2571 $MATCH_ALL, $current_test_file);
2577 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2578 "$default_server_conf_args --fw-list-all",
2579 $cmd_out_tmp, $current_test_file);
2587 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2588 'if [ `which iptables` ]; then iptables -V; fi',
2589 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2590 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2591 'if [ `which gpg` ]; then gpg --version; fi',
2592 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2596 'ls -l /usr/lib/*pcap*',
2597 'ls -l /usr/local/lib/*pcap*',
2598 'ls -l /usr/lib/*fko*',
2599 'ls -l /usr/local/lib/*fko*',
2601 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2603 if ($cmd =~ /^ldd/) {
2604 $have_gpgme++ if &file_find_regex([qr/gpgme/],
2605 $MATCH_ALL, $cmd_out_tmp);
2609 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2610 ### to enable gpg tests
2611 unless ($have_gpgme == 3) {
2612 push @tests_to_exclude, qr/GPG/;
2618 sub time_for_valgrind() {
2620 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2621 "grep valgrind |grep -v perl | grep -v grep",
2622 $cmd_out_tmp, $current_test_file)) {
2630 sub anonymize_results() {
2632 die "[*] $output_dir does not exist" unless -d $output_dir;
2633 die "[*] $logfile does not exist, has $0 been executed?"
2636 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2639 ### remove non-loopback IP addresses
2640 my $search_re = qr/\b127\.0\.0\.1\b/;
2641 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2642 $search_re = qr/\b127\.0\.0\.2\b/;
2643 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2644 $search_re = qr/\b0\.0\.0\.0\b/;
2645 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2646 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2647 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2648 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2649 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2650 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2652 ### remove hostname from any uname output
2653 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2654 system "perl -p -i -e 'undef \$/; s|$search_re" .
2655 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2657 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2658 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2661 system "tar cvfz $tarfile $logfile $output_dir";
2662 print "[+] Anonymized test results file: $tarfile\n";
2671 my $test_hr = shift;
2673 open F, "> $default_pid_file" or die $!;
2677 &server_start($test_hr);
2679 open F, "< $default_pid_file" or die $!;
2691 sub start_fwknopd() {
2692 my $test_hr = shift;
2694 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2697 die "[*] Could not fork: $!" unless defined $pid;
2701 ### we are the child, so start fwknopd
2702 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2703 $server_cmd_tmp, $server_test_file);
2709 my ($key, $file) = @_;
2711 open K, "> $file" or die "[*] Could not open $file: $!";
2712 print K "$loopback_ip: $key\n";
2713 print K "localhost: $key\n";
2714 print K "some.host.through.proxy.com: $key\n";
2720 open C, ">> $current_test_file"
2721 or die "[*] Could not open $current_test_file: $!";
2722 print C "\n" . localtime() . " [+] PID dump:\n";
2724 &run_cmd("ps auxww | grep knop |grep -v grep",
2725 $cmd_out_tmp, $current_test_file);
2730 my ($cmd, $cmd_out, $file) = @_;
2734 or die "[*] Could not open $file: $!";
2735 print F localtime() . " CMD: $cmd\n";
2739 or die "[*] Could not open $file: $!";
2740 print F localtime() . " CMD: $cmd\n";
2744 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2746 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2747 my @cmd_lines = <C>;
2750 open F, ">> $file" or die "[*] Could not open $file: $!";
2751 print F $_ for @cmd_lines;
2764 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2773 $|++; ### turn off buffering
2775 $< == 0 && $> == 0 or
2776 die "[*] $0: You must be root (or equivalent ",
2777 "UID 0 account) to effectively test fwknop";
2779 ### validate test hashes
2781 for my $test_hr (@tests) {
2782 for my $key (keys %test_keys) {
2783 if ($test_keys{$key} == $REQUIRED) {
2784 die "[*] Missing '$key' element in hash: $hash_num"
2785 unless defined $test_hr->{$key};
2787 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2793 if ($use_valgrind) {
2794 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2795 unless -e $valgrindCmd and -x $valgrindCmd;
2798 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2799 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2801 for my $name (keys %cf) {
2802 die "[*] $cf{$name} does not exist" unless -e $cf{$name};
2805 if (-d $output_dir) {
2806 if (-d "${output_dir}.last") {
2807 rmtree "${output_dir}.last"
2808 or die "[*] rmtree ${output_dir}.last $!";
2810 mkdir "${output_dir}.last"
2811 or die "[*] ${output_dir}.last: $!";
2812 for my $file (glob("$output_dir/*.test")) {
2813 if ($file =~ m|.*/(.*)|) {
2814 copy $file, "${output_dir}.last/$1" or die $!;
2817 if (-e "$output_dir/init") {
2818 copy "$output_dir/init", "${output_dir}.last/init";
2821 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2823 $saved_last_results = 1;
2825 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2827 unless (-d $run_dir) {
2828 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2831 for my $file (glob("$output_dir/*.test")) {
2832 unlink $file or die "[*] Could not unlink($file)";
2834 if (-e "$output_dir/init") {
2835 unlink "$output_dir/init" or die $!;
2839 unlink $logfile or die $!;
2842 if ($test_include) {
2843 for my $re (split /\s*,\s*/, $test_include) {
2844 push @tests_to_include, qr/$re/;
2847 if ($test_exclude) {
2848 for my $re (split /\s*,\s*/, $test_exclude) {
2849 push @tests_to_exclude, qr/$re/;
2853 ### make sure no fwknopd instance is currently running
2854 die "[*] Please stop the running fwknopd instance."
2855 if &is_fwknopd_running();
2857 unless ($enable_recompilation_warnings_check) {
2858 push @tests_to_exclude, qr/recompilation/;
2861 unless ($enable_make_distcheck) {
2862 push @tests_to_exclude, qr/distcheck/;
2865 unless ($enable_client_ip_resolve_test) {
2866 push @tests_to_exclude, qr/IP resolve/;
2869 $sudo_path = &find_command('sudo');
2871 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2872 ### disable compilation checks
2873 push @tests_to_exclude, qr/recompilation/;
2876 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2881 } elsif (/freebsd/i) {
2882 $platform = $FREEBSD;
2888 unless ($platform eq $LINUX) {
2889 push @tests_to_exclude, qr/NAT/;
2891 unless ($platform eq $FREEBSD or $platform eq $MACOSX) {
2892 push @tests_to_exclude, qr|active/expire sets|;
2895 if (-e $default_digest_file) {
2896 unlink $default_digest_file;
2902 sub identify_loopback_intf() {
2903 return if $loopback_intf;
2907 ### lo Link encap:Local Loopback
2908 ### inet addr:127.0.0.1 Mask:255.0.0.0
2909 ### inet6 addr: ::1/128 Scope:Host
2910 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2911 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2912 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2913 ### collisions:0 txqueuelen:0
2914 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2918 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2919 ### options=3<RXCSUM,TXCSUM>
2920 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2921 ### inet6 ::1 prefixlen 128
2922 ### inet 127.0.0.1 netmask 0xff000000
2923 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2926 my $found_loopback_intf = 0;
2928 my $cmd = 'ifconfig -a';
2929 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2931 if (/^(\S+?):?\s+.*loopback/i) {
2935 if (/^\S/ and $intf and not $found_loopback_intf) {
2936 ### should not happen
2939 if ($intf and /\b127\.0\.0\.1\b/) {
2940 $found_loopback_intf = 1;
2946 die "[*] could not determine loopback interface, use --loopback <name>"
2947 unless $found_loopback_intf;
2949 $loopback_intf = $intf;
2954 sub parse_valgrind_flagged_functions() {
2955 for my $file (glob("$output_dir/*.test")) {
2956 my $type = 'server';
2957 $type = 'client' if $file =~ /\d\.test/;
2958 open F, "< $file" or die $!;
2960 ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
2961 if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
2962 $valgrind_flagged_fcns{$type}{"$1 $2"}++;
2963 $valgrind_flagged_fcns_unique{$type}{$1}++;
2969 open F, ">> $current_test_file" or die $!;
2970 for my $type ('client', 'server') {
2971 print F "\n[+] fwknop $type functions (unique view):\n";
2972 next unless defined $valgrind_flagged_fcns_unique{$type};
2973 for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
2974 <=> $valgrind_flagged_fcns_unique{$type}{$a}}
2975 keys %{$valgrind_flagged_fcns_unique{$type}}) {
2976 printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
2978 print F "\n[+] fwknop $type functions (with call line numbers):\n";
2979 for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
2980 <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
2981 printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
2983 next unless defined $valgrind_flagged_fcns{$type};
2990 sub is_fw_rule_active() {
2991 my $test_hr = shift;
2993 my $conf_args = $default_server_conf_args;
2995 if ($test_hr->{'server_conf'}) {
2996 $conf_args = "-c $test_hr->{'server_conf'} -a $cf{'def_access'} " .
2997 "-d $default_digest_file -p $default_pid_file";
3000 if ($test_hr->{'no_ip_check'}) {
3001 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3002 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep _exp_},
3003 $cmd_out_tmp, $current_test_file);
3005 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3006 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
3007 $cmd_out_tmp, $current_test_file);
3013 sub is_fwknopd_running() {
3015 sleep 2 if $use_valgrind;
3017 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
3018 "--status", $cmd_out_tmp, $current_test_file);
3020 return 1 if &file_find_regex([qr/Detected\sfwknopd\sis\srunning/i],
3021 $MATCH_ALL, $cmd_out_tmp);
3026 sub stop_fwknopd() {
3028 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
3029 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
3031 if ($use_valgrind) {
3032 &time_for_valgrind();
3040 sub file_find_regex() {
3041 my ($re_ar, $match_style, $file) = @_;
3043 my $found_all_regexs = 1;
3044 my $found_single_match = 0;
3045 my @write_lines = ();
3046 my @file_lines = ();
3048 open F, "< $file" or die "[*] Could not open $file: $!";
3050 push @file_lines, $_;
3054 for my $re (@$re_ar) {
3056 for my $line (@file_lines) {
3058 push @write_lines, "[.] file_find_regex() " .
3059 "Matched '$re' with line: $line";
3061 $found_single_match = 1;
3065 push @write_lines, "[.] file_find_regex() " .
3066 "Did not match regex '$re' from regexs: '@$re_ar' " .
3067 "within file: $file\n";
3068 $found_all_regexs = 0;
3072 for my $line (@write_lines) {
3073 &write_test_file($line, $file);
3076 if ($match_style == $MATCH_ANY) {
3077 return $found_single_match;
3080 return $found_all_regexs;
3083 sub find_command() {
3087 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
3089 if (m|^(/.*$cmd)$|) {
3098 sub write_test_file() {
3099 my ($msg, $file) = @_;
3103 or die "[*] Could not open $file: $!";
3108 or die "[*] Could not open $file: $!";
3118 open F, ">> $logfile" or die $!;
3129 -A --Anonymize-results - Prepare anonymized results at:
3131 --diff - Compare the results of one test run to
3132 another. By default this compares output
3133 in ${output_dir}.last to $output_dir
3134 --diff-dir1=<path> - Left hand side of diff directory path,
3135 default is: ${output_dir}.last
3136 --diff-dir2=<path> - Right hand side of diff directory path,
3137 default is: $output_dir
3138 --include=<regex> - Specify a regex to be used over test
3139 names that must match.
3140 --exclude=<regex> - Specify a regex to be used over test
3141 names that must not match.
3142 --enable-recompile - Recompile fwknop sources and look for
3143 compilation warnings.
3144 --enable-valgrind - Run every test underneath valgrind.
3145 --List - List test names.
3146 --loopback-intf=<intf> - Specify loopback interface name (default
3147 depends on the OS where the test suite
3149 --output-dir=<path> - Path to output directory, default is:
3151 --fwknop-path=<path> - Path to fwknop binary, default is:
3153 --fwknopd-path=<path> - Path to fwknopd binary, default is:
3155 --libfko-path=<path> - Path to libfko, default is:
3157 --valgrind-path=<path> - Path to valgrind, default is:
3159 -h --help - Display usage on STDOUT and exit.