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 '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",
52 my $default_digest_file = "$run_dir/digest.cache";
53 my $default_pid_file = "$run_dir/fwknopd.pid";
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';
60 my $gpg_server_key = '361BBAD4';
61 my $gpg_client_key = '6A3FAD56';
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;
70 my $spoof_user = 'testuser';
71 #================== end config ===================
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 = ();
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 = '';
91 my $valgrind_str = '';
92 my $enable_client_ip_resolve_test = 0;
93 my $saved_last_results = 0;
95 my $enable_recompilation_warnings_check = 0;
102 my $USE_PREDEF_PKTS = 1;
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;
113 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
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,
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;
145 &identify_loopback_intf();
147 $valgrind_str = "$valgrindCmd --leak-check=full " .
148 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
150 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
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";
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";
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";
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 ";
169 my $default_server_conf_args = "-c $cf{'def'} -a $cf{'def_access'} " .
170 "-d $default_digest_file -p $default_pid_file";
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";
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";
182 ### point the compiled binaries at the local libary path
183 ### instead of any installed libfko instance
184 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
186 ### main array that defines the tests we will run
189 'category' => 'recompilation',
190 'detail' => 'recompile and look for compilation warnings',
191 'err_msg' => 'compile warnings exist',
192 'function' => \&compile_warnings,
196 'category' => 'build',
197 'subcategory' => 'client',
198 'detail' => 'binary exists',
199 'err_msg' => 'binary not found',
200 'function' => \&binary_exists,
201 'binary' => $fwknopCmd,
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,
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,
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,
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,
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,
251 'category' => 'build',
252 'subcategory' => 'server',
253 'detail' => 'binary exists',
254 'err_msg' => 'binary not found',
255 'function' => \&binary_exists,
256 'binary' => $fwknopdCmd,
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,
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,
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,
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,
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,
307 'category' => 'build',
308 'subcategory' => 'libfko',
309 'detail' => 'binary exists',
310 'err_msg' => 'binary not found',
311 'function' => \&binary_exists,
312 'binary' => $libfko_bin,
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,
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,
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,
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,
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",
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",
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",
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",
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",
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",
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",
424 'category' => 'preliminaries',
425 'detail' => 'collecting system specifics',
426 'err_msg' => 'could not get complete system specs',
427 'function' => \&specs,
428 'binary' => $fwknopdCmd,
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/],
439 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
440 "$fwknopdCmd -c $cf{'def'} " .
441 "-a $cf{'def_access'} --dump-config",
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\'/],
451 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
452 "$fwknopdCmd $default_server_conf_args " .
453 "-O $conf_dir/override_fwknopd.conf --dump-config",
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],
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",
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],
478 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
479 "$fwknopCmd -D $loopback_ip",
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],
490 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
491 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
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],
502 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
503 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
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,
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",
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",
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",
548 'category' => 'basic operations',
549 'subcategory' => 'server',
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",
558 'category' => 'basic operations',
559 'subcategory' => 'server',
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",
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",
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",
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",
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"|,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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'},
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'},
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'},
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'},
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,
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,
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,
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,
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 " .
1099 'fw_rule_created' => $NEW_RULE_REQUIRED,
1100 'fw_rule_removed' => $NEW_RULE_REMOVED,
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",
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/],
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/],
1144 'category' => 'Rijndael SPA',
1145 'subcategory' => 'server',
1146 'detail' => 'digest cache structure',
1147 'err_msg' => 'improper digest cache structure',
1148 'function' => \&digest_cache_structure,
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",
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",
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",
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",
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,
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,
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,
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,
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,
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/],
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/],
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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/],
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/],
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,
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,
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,
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,
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,
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,
1527 if ($use_valgrind) {
1530 'category' => 'valgrind output',
1531 'subcategory' => 'flagged functions',
1533 'err_msg' => 'could not parse flagged functions',
1534 'function' => \&parse_valgrind_flagged_functions,
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,
1563 &diff_test_results();
1567 ### make sure everything looks as expected before continuing
1570 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1571 " args: @args_cp\n\n"
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");
1581 ### main loop through all of the tests
1582 for my $test_hr (@tests) {
1583 &run_test($test_hr);
1586 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1588 copy $logfile, "$output_dir/$logfile" or die $!;
1592 #===================== end main =======================
1595 my $test_hr = shift;
1597 my $msg = "[$test_hr->{'category'}]";
1598 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1599 $msg .= " $test_hr->{'detail'}";
1601 return unless &process_include_exclude($msg);
1611 $current_test_file = "$output_dir/$executed.test";
1612 $server_test_file = "$output_dir/${executed}_fwknopd.test";
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");
1620 &logr("fail ($executed)\n");
1623 if ($test_hr->{'fatal'} eq $YES) {
1624 die "[*] required test failed, exiting.";
1631 sub process_include_exclude() {
1634 ### inclusions/exclusions
1635 if (@tests_to_include) {
1637 for my $test (@tests_to_include) {
1638 if ($msg =~ /$test/ or ($use_valgrind
1639 and $msg =~ /valgrind\soutput/)) {
1644 return 0 unless $found;
1646 if (@tests_to_exclude) {
1648 for my $test (@tests_to_exclude) {
1649 if ($msg =~ /$test/) {
1659 sub diff_test_results() {
1661 $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
1662 $diff_dir2 = $output_dir unless $diff_dir2;
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;
1668 my %current_tests = ();
1669 my %previous_tests = ();
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);
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";
1688 &diff_results($previous_num, $current_num);
1696 sub diff_results() {
1697 my ($previous_num, $current_num) = @_;
1699 ### edit out any valgrind "==354==" prefixes
1700 my $valgrind_search_re = qr/^==\d+==\s/;
1702 ### remove CMD timestamps
1703 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
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",
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;
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";
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";
1729 sub build_results_hash() {
1730 my ($hr, $dir) = @_;
1732 open F, "< $dir/$logfile" or die $!;
1734 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1735 $hr->{$1}{'pass_fail'} = $2;
1736 $hr->{$1}{'num'} = $3;
1742 sub compile_warnings() {
1744 ### 'make clean' as root
1745 return 0 unless &run_cmd('make -C .. clean',
1746 $cmd_out_tmp, $current_test_file);
1749 my $username = getpwuid((stat($configure_path))[4]);
1750 die "[*] Could not determine $configure_path owner"
1753 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1754 $cmd_out_tmp, $current_test_file);
1758 return 0 unless &run_cmd('make -C ..',
1759 $cmd_out_tmp, $current_test_file);
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);
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);
1773 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1774 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1775 $current_test_file);
1781 sub binary_exists() {
1782 my $test_hr = shift;
1783 return 0 unless $test_hr->{'binary'};
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)
1788 if ($test_hr->{'binary'} =~ /libfko/) {
1789 unless (-e $test_hr->{'binary'}) {
1790 my $file = "$lib_dir/libfko.dylib";
1792 $test_hr->{'binary'} = $file;
1793 $libfko_bin = $file;
1795 for my $f (glob("$lib_dir/libfko.so*")) {
1796 if (-e $f and -x $f) {
1797 $test_hr->{'binary'} = $f;
1806 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1810 sub expected_code_version() {
1811 my $test_hr = shift;
1813 unless (-e '../VERSION') {
1814 &write_test_file("[-] ../VERSION file does not exist.\n",
1815 $current_test_file);
1819 open F, '< ../VERSION' or die $!;
1822 if ($line =~ /(\d.*\d)/) {
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);
1832 sub client_send_spa_packet() {
1833 my $test_hr = shift;
1835 &write_key('fwknoptest', $local_key_file);
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);
1846 my $test_hr = shift;
1848 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1849 = &client_server_interaction($test_hr, [], $USE_CLIENT);
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;
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;
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);
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);
1878 sub spoof_username() {
1879 my $test_hr = shift;
1881 my $rv = &spa_cycle($test_hr);
1883 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1884 $MATCH_ALL, $current_test_file)) {
1888 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1889 $MATCH_ALL, $server_test_file)) {
1896 sub replay_detection() {
1897 my $test_hr = shift;
1899 ### do a complete SPA cycle and then parse the SPA packet out of the
1900 ### current test file and re-send
1902 return 0 unless &spa_cycle($test_hr);
1904 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1907 &write_test_file("[-] could not get SPA packet " .
1908 "from file: $current_test_file\n",
1909 $current_test_file);
1913 if ($test_hr->{'pkt_prefix'}) {
1914 $spa_pkt = $test_hr->{'pkt_prefix'} . $spa_pkt;
1920 'port' => $default_spa_port,
1921 'dst_ip' => $loopback_ip,
1926 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1927 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1929 $rv = 0 unless $server_was_stopped;
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);
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);
1946 sub digest_cache_structure() {
1947 my $test_hr = shift;
1950 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1952 if (&file_find_regex([qr/ASCII/i], $MATCH_ALL, $cmd_out_tmp)) {
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: $!";
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);
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);
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);
1980 &write_test_file("[+] valid digest.cache structure.\n",
1981 $current_test_file);
1987 sub server_bpf_ignore_packet() {
1988 my $test_hr = shift;
1991 my $server_was_stopped = 0;
1992 my $fw_rule_created = 0;
1993 my $fw_rule_removed = 0;
1995 unless (&client_send_spa_packet($test_hr)) {
1996 &write_test_file("[-] fwknop client execution error.\n",
1997 $current_test_file);
2001 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2004 &write_test_file("[-] could not get SPA packet " .
2005 "from file: $current_test_file\n", $current_test_file);
2012 'port' => $default_spa_port,
2013 'dst_ip' => $loopback_ip,
2018 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2019 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2021 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
2022 $MATCH_ALL, $server_test_file)) {
2029 sub altered_non_base64_spa_data() {
2030 my $test_hr = shift;
2033 my $server_was_stopped = 0;
2034 my $fw_rule_created = 0;
2035 my $fw_rule_removed = 0;
2037 unless (&client_send_spa_packet($test_hr)) {
2038 &write_test_file("[-] fwknop client execution error.\n",
2039 $current_test_file);
2043 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2046 &write_test_file("[-] could not get SPA packet " .
2047 "from file: $current_test_file\n", $current_test_file);
2051 ### alter one byte (change to a ":")
2052 $spa_pkt =~ s|^(.{3}).|$1:|;
2057 'port' => $default_spa_port,
2058 'dst_ip' => $loopback_ip,
2063 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2064 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2066 $rv = 0 unless $server_was_stopped;
2071 sub altered_base64_spa_data() {
2072 my $test_hr = shift;
2075 my $server_was_stopped = 0;
2076 my $fw_rule_created = 0;
2077 my $fw_rule_removed = 0;
2079 unless (&client_send_spa_packet($test_hr)) {
2080 &write_test_file("[-] fwknop client execution error.\n",
2081 $current_test_file);
2085 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2088 &write_test_file("[-] could not get SPA packet " .
2089 "from file: $current_test_file\n", $current_test_file);
2093 $spa_pkt =~ s|^(.{3}).|AAAA|;
2098 'port' => $default_spa_port,
2099 'dst_ip' => $loopback_ip,
2104 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2105 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2107 $rv = 0 unless $server_was_stopped;
2109 if ($fw_rule_created) {
2110 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2113 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2116 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2117 $MATCH_ALL, $server_test_file)) {
2124 sub appended_spa_data() {
2125 my $test_hr = shift;
2128 my $server_was_stopped = 0;
2129 my $fw_rule_created = 0;
2130 my $fw_rule_removed = 0;
2132 unless (&client_send_spa_packet($test_hr)) {
2133 &write_test_file("[-] fwknop client execution error.\n",
2134 $current_test_file);
2138 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2141 &write_test_file("[-] could not get SPA packet " .
2142 "from file: $current_test_file\n", $current_test_file);
2151 'port' => $default_spa_port,
2152 'dst_ip' => $loopback_ip,
2157 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2158 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2160 $rv = 0 unless $server_was_stopped;
2162 if ($fw_rule_created) {
2163 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2166 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2169 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2170 $MATCH_ALL, $server_test_file)) {
2177 sub prepended_spa_data() {
2178 my $test_hr = shift;
2181 my $server_was_stopped = 0;
2182 my $fw_rule_created = 0;
2183 my $fw_rule_removed = 0;
2185 unless (&client_send_spa_packet($test_hr)) {
2186 &write_test_file("[-] fwknop client execution error.\n",
2187 $current_test_file);
2191 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
2194 &write_test_file("[-] could not get SPA packet " .
2195 "from file: $current_test_file\n", $current_test_file);
2199 $spa_pkt = 'AAAA' . $spa_pkt;
2204 'port' => $default_spa_port,
2205 'dst_ip' => $loopback_ip,
2210 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2211 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2213 $rv = 0 unless $server_was_stopped;
2215 if ($fw_rule_created) {
2216 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2219 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2222 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2223 $MATCH_ALL, $server_test_file)) {
2230 sub server_start() {
2231 my $test_hr = shift;
2233 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2234 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2236 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
2237 $MATCH_ALL, $server_test_file)) {
2241 $rv = 0 unless $server_was_stopped;
2247 my $test_hr = shift;
2249 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2250 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2252 $rv = 0 unless $server_was_stopped;
2257 sub server_packet_limit() {
2258 my $test_hr = shift;
2263 'port' => $default_spa_port,
2264 'dst_ip' => $loopback_ip,
2269 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2270 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2272 if (&is_fwknopd_running()) {
2277 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2278 $MATCH_ALL, $server_test_file)) {
2282 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2283 $MATCH_ALL, $server_test_file)) {
2290 sub server_ignore_small_packets() {
2291 my $test_hr = shift;
2296 'port' => $default_spa_port,
2297 'dst_ip' => $loopback_ip,
2298 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2302 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2303 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2307 if (&is_fwknopd_running()) {
2315 sub client_server_interaction() {
2316 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2319 my $server_was_stopped = 1;
2320 my $fw_rule_created = 1;
2321 my $fw_rule_removed = 0;
2323 ### start fwknopd to monitor for the SPA packet over the loopback interface
2324 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2326 ### give fwknopd a chance to parse its config and start sniffing
2327 ### on the loopback interface
2328 if ($use_valgrind) {
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);
2343 &send_packets($pkts_hr);
2346 ### check to see if the SPA packet resulted in a new fw access rule
2348 while (not &is_fw_rule_active($test_hr)) {
2349 &write_test_file("[-] new fw rule does not exist.\n",
2350 $current_test_file);
2356 $fw_rule_created = 0;
2357 $fw_rule_removed = 0;
2360 &time_for_valgrind() if $use_valgrind;
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);
2369 &write_test_file("[+] new fw rule timed out.\n",
2370 $current_test_file);
2371 $fw_rule_removed = 1;
2375 if (&is_fwknopd_running()) {
2377 unless (&file_find_regex([qr/Got\sSIGTERM/],
2378 $MATCH_ALL, $server_test_file)) {
2379 $server_was_stopped = 0;
2382 &write_test_file("[-] server is not running.\n",
2383 $current_test_file);
2384 $server_was_stopped = 0;
2387 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2390 sub get_spa_packet_from_file() {
2395 my $found_trigger_line = 0;
2396 open F, "< $file" or die "[*] Could not open file $file: $!";
2398 if (/final\spacked/i) {
2399 $found_trigger_line = 1;
2402 next unless $found_trigger_line;
2404 ### the next line with non whitespace is the SPA packet
2415 sub send_packets() {
2416 my $pkts_ar = shift;
2418 open F, ">> $current_test_file" or die $!;
2419 print F "[+] send_packets(): Sending the following packets...\n";
2420 print F Dumper $pkts_ar;
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'},
2430 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2431 "socket to $pkt_hr->{'dst_ip'}: $!";
2433 $socket->send($pkt_hr->{'data'});
2436 } elsif ($pkt_hr->{'proto'} eq 'http') {
2438 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2442 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2447 sub generic_exec() {
2448 my $test_hr = shift;
2452 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2453 $cmd_out_tmp, $current_test_file);
2455 if ($test_hr->{'exec_err'} eq $YES) {
2456 $rv = 0 if $exec_rv;
2458 $rv = 0 unless $exec_rv;
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);
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);
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);
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);
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);
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);
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);
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);
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',
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*',
2557 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2559 if ($cmd =~ /^ldd/) {
2560 $have_gpgme++ if &file_find_regex([qr/gpgme/],
2561 $MATCH_ALL, $cmd_out_tmp);
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";
2574 sub time_for_valgrind() {
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)) {
2586 sub anonymize_results() {
2588 die "[*] $output_dir does not exist" unless -d $output_dir;
2589 die "[*] $logfile does not exist, has $0 been executed?"
2592 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
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";
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";
2613 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2614 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2617 system "tar cvfz $tarfile $logfile $output_dir";
2618 print "[+] Anonymized test results file: $tarfile\n";
2627 my $test_hr = shift;
2629 open F, "> $default_pid_file" or die $!;
2633 &server_start($test_hr);
2635 open F, "< $default_pid_file" or die $!;
2647 sub start_fwknopd() {
2648 my $test_hr = shift;
2650 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2653 die "[*] Could not fork: $!" unless defined $pid;
2657 ### we are the child, so start fwknopd
2658 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2659 $server_cmd_tmp, $server_test_file);
2665 my ($key, $file) = @_;
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";
2676 open C, ">> $current_test_file"
2677 or die "[*] Could not open $current_test_file: $!";
2678 print C "\n" . localtime() . " [+] PID dump:\n";
2680 &run_cmd("ps auxww | grep knop |grep -v grep",
2681 $cmd_out_tmp, $current_test_file);
2686 my ($cmd, $cmd_out, $file) = @_;
2690 or die "[*] Could not open $file: $!";
2691 print F localtime() . " CMD: $cmd\n";
2695 or die "[*] Could not open $file: $!";
2696 print F localtime() . " CMD: $cmd\n";
2700 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2702 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2703 my @cmd_lines = <C>;
2706 open F, ">> $file" or die "[*] Could not open $file: $!";
2707 print F $_ for @cmd_lines;
2720 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2729 $|++; ### turn off buffering
2731 $< == 0 && $> == 0 or
2732 die "[*] $0: You must be root (or equivalent ",
2733 "UID 0 account) to effectively test fwknop";
2735 ### validate test hashes
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};
2743 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2749 if ($use_valgrind) {
2750 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2751 unless -e $valgrindCmd and -x $valgrindCmd;
2754 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2755 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2757 for my $name (keys %cf) {
2758 die "[*] $cf{$name} does not exist" unless -e $cf{$name};
2761 if (-d $output_dir) {
2762 if (-d "${output_dir}.last") {
2763 rmtree "${output_dir}.last"
2764 or die "[*] rmtree ${output_dir}.last $!";
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 $!;
2773 if (-e "$output_dir/init") {
2774 copy "$output_dir/init", "${output_dir}.last/init";
2777 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2779 $saved_last_results = 1;
2781 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2783 unless (-d $run_dir) {
2784 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2787 for my $file (glob("$output_dir/*.test")) {
2788 unlink $file or die "[*] Could not unlink($file)";
2790 if (-e "$output_dir/init") {
2791 unlink "$output_dir/init" or die $!;
2795 unlink $logfile or die $!;
2798 if ($test_include) {
2799 @tests_to_include = split /\s*,\s*/, $test_include;
2801 if ($test_exclude) {
2802 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2805 ### make sure no fwknopd instance is currently running
2806 die "[*] Please stop the running fwknopd instance."
2807 if &is_fwknopd_running();
2809 unless ($enable_recompilation_warnings_check) {
2810 push @tests_to_exclude, 'recompilation';
2813 unless ($enable_client_ip_resolve_test) {
2814 push @tests_to_exclude, 'IP resolve';
2817 $sudo_path = &find_command('sudo');
2819 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2820 ### disable compilation checks
2821 push @tests_to_exclude, 'recompilation';
2824 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2827 $platform = 'linux';
2833 unless ($platform eq 'linux') {
2834 push @tests_to_exclude, 'NAT';
2837 if (-e $default_digest_file) {
2838 unlink $default_digest_file;
2844 sub identify_loopback_intf() {
2845 return if $loopback_intf;
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)
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>
2868 my $found_loopback_intf = 0;
2870 my $cmd = 'ifconfig -a';
2871 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2873 if (/^(\S+?):?\s+.*loopback/i) {
2877 if (/^\S/ and $intf and not $found_loopback_intf) {
2878 ### should not happen
2881 if ($intf and /\b127\.0\.0\.1\b/) {
2882 $found_loopback_intf = 1;
2888 die "[*] could not determine loopback interface, use --loopback <name>"
2889 unless $found_loopback_intf;
2891 $loopback_intf = $intf;
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 $!;
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}++;
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;
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;
2925 next unless defined $valgrind_flagged_fcns{$type};
2932 sub is_fw_rule_active() {
2933 my $test_hr = shift;
2935 my $conf_args = $default_server_conf_args;
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";
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);
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);
2955 sub is_fwknopd_running() {
2957 sleep 2 if $use_valgrind;
2959 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2960 "--status", $cmd_out_tmp, $current_test_file);
2962 return 1 if &file_find_regex([qr/Detected\sfwknopd\sis\srunning/i],
2963 $MATCH_ALL, $cmd_out_tmp);
2968 sub stop_fwknopd() {
2970 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2971 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2973 if ($use_valgrind) {
2974 &time_for_valgrind();
2982 sub file_find_regex() {
2983 my ($re_ar, $match_style, $file) = @_;
2985 my $found_all_regexs = 1;
2986 my $found_single_match = 0;
2987 my @write_lines = ();
2988 my @file_lines = ();
2990 open F, "< $file" or die "[*] Could not open $file: $!";
2992 push @file_lines, $_;
2996 for my $re (@$re_ar) {
2998 for my $line (@file_lines) {
3000 push @write_lines, "[.] file_find_regex() " .
3001 "Matched '$re' with line: $line";
3003 $found_single_match = 1;
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;
3014 for my $line (@write_lines) {
3015 &write_test_file($line, $file);
3018 if ($match_style == $MATCH_ANY) {
3019 return $found_single_match;
3022 return $found_all_regexs;
3025 sub find_command() {
3029 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
3031 if (m|^(/.*$cmd)$|) {
3040 sub write_test_file() {
3041 my ($msg, $file) = @_;
3045 or die "[*] Could not open $file: $!";
3050 or die "[*] Could not open $file: $!";
3060 open F, ">> $logfile" or die $!;
3071 -A --Anonymize-results - Prepare anonymized results at:
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
3091 --output-dir=<path> - Path to output directory, default is:
3093 --fwknop-path=<path> - Path to fwknop binary, default is:
3095 --fwknopd-path=<path> - Path to fwknopd binary, default is:
3097 --libfko-path=<path> - Path to libfko, default is:
3099 --valgrind-path=<path> - Path to valgrind, default is:
3101 -h --help - Display usage on STDOUT and exit.