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";
22 my $nat_conf = "$conf_dir/nat_fwknopd.conf";
23 my $default_conf = "$conf_dir/default_fwknopd.conf";
24 my $default_access_conf = "$conf_dir/default_access.conf";
25 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
26 my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
27 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
28 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
29 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
30 my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
31 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
32 my $default_digest_file = "$run_dir/digest.cache";
33 my $default_pid_file = "$run_dir/fwknopd.pid";
34 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
35 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
36 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
37 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
38 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
39 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
40 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
41 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
42 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
43 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
44 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
45 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
46 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
47 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
49 my $fwknopCmd = '../client/.libs/fwknop';
50 my $fwknopdCmd = '../server/.libs/fwknopd';
51 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
52 my $valgrindCmd = '/usr/bin/valgrind';
54 my $gpg_server_key = '361BBAD4';
55 my $gpg_client_key = '6A3FAD56';
57 my $loopback_ip = '127.0.0.1';
58 my $fake_ip = '127.0.0.2';
59 my $internal_nat_host = '192.168.1.2';
60 my $force_nat_host = '192.168.1.123';
61 my $default_spa_port = 62201;
62 my $non_std_spa_port = 12345;
64 my $spoof_user = 'testuser';
65 #================== end config ===================
70 my $test_include = '';
71 my @tests_to_include = ();
72 my $test_exclude = '';
73 my @tests_to_exclude = ();
74 my %valgrind_flagged_fcns = ();
75 my %valgrind_flagged_fcns_unique = ();
79 my $loopback_intf = '';
80 my $anonymize_results = 0;
81 my $current_test_file = "$output_dir/init";
82 my $tarfile = 'test_fwknop.tar.gz';
83 my $server_test_file = '';
85 my $valgrind_str = '';
86 my $saved_last_results = 0;
88 my $enable_recompilation_warnings_check = 0;
95 my $USE_PREDEF_PKTS = 1;
99 my $NEW_RULE_REQUIRED = 1;
100 my $REQUIRE_NO_NEW_RULE = 2;
101 my $NEW_RULE_REMOVED = 1;
102 my $REQUIRE_NO_NEW_REMOVED = 2;
104 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
108 exit 1 unless GetOptions(
109 'Anonymize-results' => \$anonymize_results,
110 'fwknop-path=s' => \$fwknopCmd,
111 'fwknopd-path=s' => \$fwknopdCmd,
112 'libfko-path=s' => \$libfko_bin,
113 'loopback-intf=s' => \$loopback_intf,
114 'test-include=s' => \$test_include,
115 'include=s' => \$test_include, ### synonym
116 'test-exclude=s' => \$test_exclude,
117 'exclude=s' => \$test_exclude, ### synonym
118 'enable-recompile-check' => \$enable_recompilation_warnings_check,
119 'List-mode' => \$list_mode,
120 'enable-valgrind' => \$use_valgrind,
121 'valgrind-path=s' => \$valgrindCmd,
122 'output-dir=s' => \$output_dir,
123 'diff' => \$diff_mode,
124 'diff-dir1=s' => \$diff_dir1,
125 'diff-dir2=s' => \$diff_dir2,
131 ### create an anonymized tar file of test suite results that can be
132 ### emailed around to assist in debugging fwknop communications
133 exit &anonymize_results() if $anonymize_results;
135 &identify_loopback_intf();
137 $valgrind_str = "$valgrindCmd --leak-check=full " .
138 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
140 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
142 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
143 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
144 "$local_key_file --verbose --verbose";
146 my $default_client_gpg_args = "$default_client_args " .
147 "--gpg-recipient-key $gpg_server_key " .
148 "--gpg-signer-key $gpg_client_key " .
149 "--gpg-home-dir $gpg_client_home_dir";
151 my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
152 "-d $default_digest_file -p $default_pid_file";
154 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
155 "$valgrind_str $fwknopdCmd -c $default_conf " .
156 "-a $gpg_access_conf $intf_str " .
157 "-d $default_digest_file -p $default_pid_file";
159 ### point the compiled binaries at the local libary path
160 ### instead of any installed libfko instance
161 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
163 ### main array that defines the tests we will run
166 'category' => 'recompilation',
167 'detail' => 'recompile and look for compilation warnings',
168 'err_msg' => 'compile warnings exist',
169 'function' => \&compile_warnings,
173 'category' => 'build',
174 'subcategory' => 'client',
175 'detail' => 'binary exists',
176 'err_msg' => 'binary not found',
177 'function' => \&binary_exists,
178 'binary' => $fwknopCmd,
182 'category' => 'build security',
183 'subcategory' => 'client',
184 'detail' => 'Position Independent Executable (PIE)',
185 'err_msg' => 'non PIE binary (fwknop client)',
186 'function' => \&pie_binary,
187 'binary' => $fwknopCmd,
191 'category' => 'build security',
192 'subcategory' => 'client',
193 'detail' => 'stack protected binary',
194 'err_msg' => 'non stack protected binary (fwknop client)',
195 'function' => \&stack_protected_binary,
196 'binary' => $fwknopCmd,
200 'category' => 'build security',
201 'subcategory' => 'client',
202 'detail' => 'fortify source functions',
203 'err_msg' => 'source functions not fortified (fwknop client)',
204 'function' => \&fortify_source_functions,
205 'binary' => $fwknopCmd,
209 'category' => 'build security',
210 'subcategory' => 'client',
211 'detail' => 'read-only relocations',
212 'err_msg' => 'no read-only relocations (fwknop client)',
213 'function' => \&read_only_relocations,
214 'binary' => $fwknopCmd,
218 'category' => 'build security',
219 'subcategory' => 'client',
220 'detail' => 'immediate binding',
221 'err_msg' => 'no immediate binding (fwknop client)',
222 'function' => \&immediate_binding,
223 'binary' => $fwknopCmd,
228 'category' => 'build',
229 'subcategory' => 'server',
230 'detail' => 'binary exists',
231 'err_msg' => 'binary not found',
232 'function' => \&binary_exists,
233 'binary' => $fwknopdCmd,
238 'category' => 'build security',
239 'subcategory' => 'server',
240 'detail' => 'Position Independent Executable (PIE)',
241 'err_msg' => 'non PIE binary (fwknopd server)',
242 'function' => \&pie_binary,
243 'binary' => $fwknopdCmd,
247 'category' => 'build security',
248 'subcategory' => 'server',
249 'detail' => 'stack protected binary',
250 'err_msg' => 'non stack protected binary (fwknopd server)',
251 'function' => \&stack_protected_binary,
252 'binary' => $fwknopdCmd,
256 'category' => 'build security',
257 'subcategory' => 'server',
258 'detail' => 'fortify source functions',
259 'err_msg' => 'source functions not fortified (fwknopd server)',
260 'function' => \&fortify_source_functions,
261 'binary' => $fwknopdCmd,
265 'category' => 'build security',
266 'subcategory' => 'server',
267 'detail' => 'read-only relocations',
268 'err_msg' => 'no read-only relocations (fwknopd server)',
269 'function' => \&read_only_relocations,
270 'binary' => $fwknopdCmd,
274 'category' => 'build security',
275 'subcategory' => 'server',
276 'detail' => 'immediate binding',
277 'err_msg' => 'no immediate binding (fwknopd server)',
278 'function' => \&immediate_binding,
279 'binary' => $fwknopdCmd,
284 'category' => 'build',
285 'subcategory' => 'libfko',
286 'detail' => 'binary exists',
287 'err_msg' => 'binary not found',
288 'function' => \&binary_exists,
289 'binary' => $libfko_bin,
293 'category' => 'build security',
294 'subcategory' => 'libfko',
295 'detail' => 'stack protected binary',
296 'err_msg' => 'non stack protected binary (libfko)',
297 'function' => \&stack_protected_binary,
298 'binary' => $libfko_bin,
302 'category' => 'build security',
303 'subcategory' => 'libfko',
304 'detail' => 'fortify source functions',
305 'err_msg' => 'source functions not fortified (libfko)',
306 'function' => \&fortify_source_functions,
307 'binary' => $libfko_bin,
311 'category' => 'build security',
312 'subcategory' => 'libfko',
313 'detail' => 'read-only relocations',
314 'err_msg' => 'no read-only relocations (libfko)',
315 'function' => \&read_only_relocations,
316 'binary' => $libfko_bin,
320 'category' => 'build security',
321 'subcategory' => 'libfko',
322 'detail' => 'immediate binding',
323 'err_msg' => 'no immediate binding (libfko)',
324 'function' => \&immediate_binding,
325 'binary' => $libfko_bin,
330 'category' => 'preliminaries',
331 'subcategory' => 'client',
332 'detail' => 'usage info',
333 'err_msg' => 'could not get usage info',
334 'function' => \&generic_exec,
335 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
339 'category' => 'preliminaries',
340 'subcategory' => 'client',
341 'detail' => 'getopt() no such argument',
342 'err_msg' => 'getopt() allowed non-existant argument',
343 'function' => \&generic_exec,
344 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
349 'category' => 'preliminaries',
350 'subcategory' => 'client',
351 'detail' => '--test mode, packet not sent',
352 'err_msg' => '--test mode, packet sent?',
353 'function' => \&generic_exec,
354 'positive_output_matches' => [qr/test\smode\senabled/],
355 'cmdline' => "$default_client_args --test",
360 'category' => 'preliminaries',
361 'subcategory' => 'client',
362 'detail' => 'expected code version',
363 'err_msg' => 'code version mis-match',
364 'function' => \&expected_code_version,
365 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
370 'category' => 'preliminaries',
371 'subcategory' => 'server',
372 'detail' => 'usage info',
373 'err_msg' => 'could not get usage info',
374 'function' => \&generic_exec,
375 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
379 'category' => 'preliminaries',
380 'subcategory' => 'server',
381 'detail' => 'getopt() no such argument',
382 'err_msg' => 'getopt() allowed non-existant argument',
383 'function' => \&generic_exec,
384 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
390 'category' => 'preliminaries',
391 'subcategory' => 'server',
392 'detail' => 'expected code version',
393 'err_msg' => 'code version mis-match',
394 'function' => \&expected_code_version,
395 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
396 "$fwknopdCmd -c $default_conf -a " .
397 "$default_access_conf --version",
401 'category' => 'preliminaries',
402 'detail' => 'collecting system specifics',
403 'err_msg' => 'could not get complete system specs',
404 'function' => \&specs,
405 'binary' => $fwknopdCmd,
410 'category' => 'basic operations',
411 'detail' => 'dump config',
412 'err_msg' => 'could not dump configuration',
413 'function' => \&generic_exec,
414 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
416 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
417 "$fwknopdCmd -c $default_conf " .
418 "-a $default_access_conf --dump-config",
422 'category' => 'basic operations',
423 'detail' => 'override config',
424 'err_msg' => 'could not override configuration',
425 'function' => \&generic_exec,
426 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
428 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
429 "$fwknopdCmd $default_server_conf_args " .
430 "-O $conf_dir/override_fwknopd.conf --dump-config",
435 'category' => 'basic operations',
436 'subcategory' => 'client',
437 'detail' => '--get-key path validation',
438 'err_msg' => 'accepted improper --get-key path',
439 'function' => \&generic_exec,
440 'positive_output_matches' => [qr/could\snot\sopen/i],
442 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
443 "$fwknopCmd -A tcp/22 -s $fake_ip " .
444 "-D $loopback_ip --get-key not/there",
448 'category' => 'basic operations',
449 'subcategory' => 'client',
450 'detail' => 'require [-s|-R|-a]',
451 'err_msg' => 'allowed null allow IP',
452 'function' => \&generic_exec,
453 'positive_output_matches' => [qr/must\suse\sone\sof/i],
455 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
456 "$fwknopCmd -D $loopback_ip",
460 'category' => 'basic operations',
461 'subcategory' => 'client',
462 'detail' => '--allow-ip <IP> valid IP',
463 'err_msg' => 'permitted invalid --allow-ip arg',
464 'function' => \&generic_exec,
465 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
467 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
468 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
472 'category' => 'basic operations',
473 'subcategory' => 'client',
474 'detail' => '-A <proto>/<port> specification',
475 'err_msg' => 'permitted invalid -A <proto>/<port>',
476 'function' => \&generic_exec,
477 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
479 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
480 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
484 'category' => 'basic operations',
485 'subcategory' => 'client',
486 'detail' => 'generate SPA packet',
487 'err_msg' => 'could not generate SPA packet',
488 'function' => \&client_send_spa_packet,
489 'cmdline' => $default_client_args,
494 'category' => 'basic operations',
495 'subcategory' => 'server',
496 'detail' => 'list current fwknopd fw rules',
497 'err_msg' => 'could not list current fwknopd fw rules',
498 'function' => \&generic_exec,
499 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
500 "$fwknopdCmd $default_server_conf_args --fw-list",
504 'category' => 'basic operations',
505 'subcategory' => 'server',
506 'detail' => 'list all current fw rules',
507 'err_msg' => 'could not list all current fw rules',
508 'function' => \&generic_exec,
509 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
510 "$fwknopdCmd $default_server_conf_args --fw-list-all",
514 'category' => 'basic operations',
515 'subcategory' => 'server',
516 'detail' => 'flush current firewall rules',
517 'err_msg' => 'could not flush current fw rules',
518 'function' => \&generic_exec,
519 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
520 "$fwknopdCmd $default_server_conf_args --fw-flush",
525 'category' => 'basic operations',
526 'subcategory' => 'server',
528 'err_msg' => 'start error',
529 'function' => \&server_start,
530 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
531 "$fwknopdCmd $default_server_conf_args $intf_str",
535 'category' => 'basic operations',
536 'subcategory' => 'server',
538 'err_msg' => 'stop error',
539 'function' => \&server_stop,
540 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
541 "$fwknopdCmd $default_server_conf_args $intf_str",
545 'category' => 'basic operations',
546 'subcategory' => 'server',
547 'detail' => 'write PID',
548 'err_msg' => 'did not write PID',
549 'function' => \&write_pid,
550 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
551 "$fwknopdCmd $default_server_conf_args $intf_str",
556 'category' => 'basic operations',
557 'subcategory' => 'server',
558 'detail' => '--packet-limit 1 exit',
559 'err_msg' => 'did not exit after one packet',
560 'function' => \&server_packet_limit,
561 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
562 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
566 'category' => 'basic operations',
567 'subcategory' => 'server',
568 'detail' => 'ignore packets < min SPA len (140)',
569 'err_msg' => 'did not ignore small packets',
570 'function' => \&server_ignore_small_packets,
571 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
572 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
576 'category' => 'basic operations',
577 'subcategory' => 'server',
578 'detail' => '-P bpf filter ignore packet',
579 'err_msg' => 'filter did not ignore packet',
580 'function' => \&server_bpf_ignore_packet,
581 'cmdline' => $default_client_args,
582 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
583 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
584 qq|-P "udp port $non_std_spa_port"|,
589 'category' => 'Rijndael SPA',
590 'subcategory' => 'client+server',
591 'detail' => 'complete cycle (tcp/22 ssh)',
592 'err_msg' => 'could not complete SPA cycle',
593 'function' => \&spa_cycle,
594 'cmdline' => $default_client_args,
595 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
596 "$fwknopdCmd $default_server_conf_args $intf_str",
597 'fw_rule_created' => $NEW_RULE_REQUIRED,
598 'fw_rule_removed' => $NEW_RULE_REMOVED,
602 'category' => 'Rijndael SPA',
603 'subcategory' => 'client+server',
604 'detail' => 'dual usage access key (tcp/80 http)',
605 'err_msg' => 'could not complete SPA cycle',
606 'function' => \&spa_cycle,
607 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
608 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
609 "$local_key_file --verbose --verbose",
610 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
611 "$fwknopdCmd -c $default_conf -a $dual_key_usage_access_conf " .
612 "-d $default_digest_file -p $default_pid_file $intf_str",
613 ### check for the first stanza that does not allow tcp/80 - the
614 ### second stanza allows this
615 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
616 'fw_rule_created' => $NEW_RULE_REQUIRED,
617 'fw_rule_removed' => $NEW_RULE_REMOVED,
621 'category' => 'Rijndael SPA',
622 'subcategory' => 'client+server',
623 'detail' => 'packet aging (past) (tcp/22 ssh)',
624 'err_msg' => 'old SPA packet accepted',
625 'function' => \&spa_cycle,
626 'cmdline' => "$default_client_args --time-offset-minus 300s",
627 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
628 "$fwknopdCmd $default_server_conf_args $intf_str",
629 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
630 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
634 'category' => 'Rijndael SPA',
635 'subcategory' => 'client+server',
636 'detail' => 'packet aging (future) (tcp/22 ssh)',
637 'err_msg' => 'future SPA packet accepted',
638 'function' => \&spa_cycle,
639 'cmdline' => "$default_client_args --time-offset-plus 300s",
640 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
641 "$fwknopdCmd $default_server_conf_args $intf_str",
642 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
643 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
647 'category' => 'Rijndael SPA',
648 'subcategory' => 'client+server',
649 'detail' => 'expired stanza (tcp/22 ssh)',
650 'err_msg' => 'SPA packet accepted',
651 'function' => \&spa_cycle,
652 'cmdline' => $default_client_args,
653 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
654 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
655 "-d $default_digest_file -p $default_pid_file $intf_str",
656 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
657 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
661 'category' => 'Rijndael SPA',
662 'subcategory' => 'client+server',
663 'detail' => 'invalid expire date (tcp/22 ssh)',
664 'err_msg' => 'SPA packet accepted',
665 'function' => \&spa_cycle,
666 'cmdline' => $default_client_args,
667 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
668 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
669 "-d $default_digest_file -p $default_pid_file $intf_str",
670 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
671 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
675 'category' => 'Rijndael SPA',
676 'subcategory' => 'client+server',
677 'detail' => 'expired epoch stanza (tcp/22 ssh)',
678 'err_msg' => 'SPA packet accepted',
679 'function' => \&spa_cycle,
680 'cmdline' => $default_client_args,
681 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
682 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
683 "-d $default_digest_file -p $default_pid_file $intf_str",
684 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
685 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
689 'category' => 'Rijndael SPA',
690 'subcategory' => 'client+server',
691 'detail' => 'future expired stanza (tcp/22 ssh)',
692 'err_msg' => 'SPA packet not accepted',
693 'function' => \&spa_cycle,
694 'cmdline' => $default_client_args,
695 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
696 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
697 "-d $default_digest_file -p $default_pid_file $intf_str",
698 'fw_rule_created' => $NEW_RULE_REQUIRED,
699 'fw_rule_removed' => $NEW_RULE_REMOVED,
704 'category' => 'Rijndael SPA',
705 'subcategory' => 'client+server',
706 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
707 'err_msg' => "improper OPEN_PORTS result",
708 'function' => \&spa_cycle,
709 'cmdline' => $default_client_args,
710 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
711 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
712 "-d $default_digest_file -p $default_pid_file $intf_str",
713 'fw_rule_created' => $NEW_RULE_REQUIRED,
714 'fw_rule_removed' => $NEW_RULE_REMOVED,
718 'category' => 'Rijndael SPA',
719 'subcategory' => 'client+server',
720 'detail' => 'OPEN_PORTS mismatch',
721 'err_msg' => "SPA packet accepted",
722 'function' => \&spa_cycle,
723 'cmdline' => $default_client_args,
724 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
725 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
726 "-d $default_digest_file -p $default_pid_file $intf_str",
727 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
728 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
732 'category' => 'Rijndael SPA',
733 'subcategory' => 'client+server',
734 'detail' => 'require user (tcp/22 ssh)',
735 'err_msg' => "missed require user criteria",
736 'function' => \&spa_cycle,
737 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
738 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
739 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
740 "-d $default_digest_file -p $default_pid_file $intf_str",
741 'fw_rule_created' => $NEW_RULE_REQUIRED,
742 'fw_rule_removed' => $NEW_RULE_REMOVED,
746 'category' => 'Rijndael SPA',
747 'subcategory' => 'client+server',
748 'detail' => 'user mismatch (tcp/22 ssh)',
749 'err_msg' => "improper user accepted for access",
750 'function' => \&user_mismatch,
751 'function' => \&spa_cycle,
752 'cmdline' => $default_client_args,
753 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
754 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
755 "-d $default_digest_file -p $default_pid_file $intf_str",
756 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
757 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
761 'category' => 'Rijndael SPA',
762 'subcategory' => 'client+server',
763 'detail' => 'require src (tcp/22 ssh)',
764 'err_msg' => "fw rule not created",
765 'function' => \&spa_cycle,
766 'cmdline' => $default_client_args,
767 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
768 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
769 "-d $default_digest_file -p $default_pid_file $intf_str",
770 'fw_rule_created' => $NEW_RULE_REQUIRED,
771 'fw_rule_removed' => $NEW_RULE_REMOVED,
775 'category' => 'Rijndael SPA',
776 'subcategory' => 'client+server',
777 'detail' => 'mismatch require src (tcp/22 ssh)',
778 'err_msg' => "fw rule created",
779 'function' => \&spa_cycle,
780 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
781 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
782 "$local_key_file --verbose --verbose",
783 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
784 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
785 "-d $default_digest_file -p $default_pid_file $intf_str",
786 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
787 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
792 'category' => 'Rijndael SPA',
793 'subcategory' => 'client+server',
794 'detail' => 'IP filtering (tcp/22 ssh)',
795 'err_msg' => "did not filter $loopback_ip",
796 'function' => \&spa_cycle,
797 'cmdline' => $default_client_args,
798 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
799 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
800 "-d $default_digest_file -p $default_pid_file $intf_str",
801 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
802 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
806 'category' => 'Rijndael SPA',
807 'subcategory' => 'client+server',
808 'detail' => 'subnet filtering (tcp/22 ssh)',
809 'err_msg' => "did not filter $loopback_ip",
810 'function' => \&spa_cycle,
811 'cmdline' => $default_client_args,
812 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
813 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
814 "-d $default_digest_file -p $default_pid_file $intf_str",
815 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
816 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
820 'category' => 'Rijndael SPA',
821 'subcategory' => 'client+server',
822 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
823 'err_msg' => "did not filter $loopback_ip",
824 'function' => \&spa_cycle,
825 'cmdline' => $default_client_args,
826 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
827 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
828 "-d $default_digest_file -p $default_pid_file $intf_str",
829 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
830 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
834 'category' => 'Rijndael SPA',
835 'subcategory' => 'client+server',
836 'detail' => 'IP match (tcp/22 ssh)',
837 'err_msg' => "did not filter $loopback_ip",
838 'function' => \&spa_cycle,
839 'cmdline' => $default_client_args,
840 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
841 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
842 "-d $default_digest_file -p $default_pid_file $intf_str",
843 'fw_rule_created' => $NEW_RULE_REQUIRED,
844 'fw_rule_removed' => $NEW_RULE_REMOVED,
848 'category' => 'Rijndael SPA',
849 'subcategory' => 'client+server',
850 'detail' => 'subnet match (tcp/22 ssh)',
851 'err_msg' => "did not filter $loopback_ip",
852 'function' => \&spa_cycle,
853 'cmdline' => $default_client_args,
854 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
855 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
856 "-d $default_digest_file -p $default_pid_file $intf_str",
857 'fw_rule_created' => $NEW_RULE_REQUIRED,
858 'fw_rule_removed' => $NEW_RULE_REMOVED,
862 'category' => 'Rijndael SPA',
863 'subcategory' => 'client+server',
864 'detail' => 'multi IP/net match (tcp/22 ssh)',
865 'err_msg' => "did not filter $loopback_ip",
866 'function' => \&spa_cycle,
867 'cmdline' => $default_client_args,
868 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
869 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
870 "-d $default_digest_file -p $default_pid_file $intf_str",
871 'fw_rule_created' => $NEW_RULE_REQUIRED,
872 'fw_rule_removed' => $NEW_RULE_REMOVED,
876 'category' => 'Rijndael SPA',
877 'subcategory' => 'client+server',
878 'detail' => 'multi access stanzas (tcp/22 ssh)',
879 'err_msg' => "could not complete SPA cycle",
880 'function' => \&spa_cycle,
881 'cmdline' => $default_client_args,
882 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
883 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
884 "-d $default_digest_file -p $default_pid_file $intf_str",
885 'fw_rule_created' => $NEW_RULE_REQUIRED,
886 'fw_rule_removed' => $NEW_RULE_REMOVED,
890 'category' => 'Rijndael SPA',
891 'subcategory' => 'client+server',
892 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
893 'err_msg' => "could not complete SPA cycle",
894 'function' => \&spa_cycle,
895 'cmdline' => $default_client_args,
896 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
897 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
898 "-d $default_digest_file -p $default_pid_file $intf_str",
899 'fw_rule_created' => $NEW_RULE_REQUIRED,
900 'fw_rule_removed' => $NEW_RULE_REMOVED,
905 'category' => 'Rijndael SPA',
906 'subcategory' => 'client+server',
907 'detail' => "non-enabled NAT (tcp/22 ssh)",
908 'err_msg' => "SPA packet not filtered",
909 'function' => \&spa_cycle,
910 'cmdline' => "$default_client_args -N $internal_nat_host:22",
911 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
912 "$fwknopdCmd $default_server_conf_args $intf_str",
913 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
914 'server_conf' => $nat_conf,
915 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
919 'category' => 'Rijndael SPA',
920 'subcategory' => 'client+server',
921 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
922 'err_msg' => "could not complete NAT SPA cycle",
923 'function' => \&spa_cycle,
924 'cmdline' => "$default_client_args -N $internal_nat_host:22",
925 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
926 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
927 "-d $default_digest_file -p $default_pid_file $intf_str",
928 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
929 'fw_rule_created' => $NEW_RULE_REQUIRED,
930 'fw_rule_removed' => $NEW_RULE_REMOVED,
931 'server_conf' => $nat_conf,
936 'category' => 'Rijndael SPA',
937 'subcategory' => 'client+server',
938 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
939 'err_msg' => "could not complete NAT SPA cycle",
940 'function' => \&spa_cycle,
941 'cmdline' => $default_client_args,
942 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
943 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
944 "-d $default_digest_file -p $default_pid_file $intf_str",
945 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
946 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
947 'fw_rule_created' => $NEW_RULE_REQUIRED,
948 'fw_rule_removed' => $NEW_RULE_REMOVED,
949 'server_conf' => $nat_conf,
954 'category' => 'Rijndael SPA',
955 'subcategory' => 'client+server',
956 'detail' => 'complete cycle (tcp/23 telnet)',
957 'err_msg' => 'could not complete SPA cycle',
958 'function' => \&spa_cycle,
959 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
960 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
961 "$local_key_file --verbose --verbose",
962 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
963 "$fwknopdCmd $default_server_conf_args $intf_str",
964 'fw_rule_created' => $NEW_RULE_REQUIRED,
965 'fw_rule_removed' => $NEW_RULE_REMOVED,
969 'category' => 'Rijndael SPA',
970 'subcategory' => 'client+server',
971 'detail' => 'complete cycle (tcp/9418 git)',
972 'err_msg' => 'could not complete SPA cycle',
973 'function' => \&spa_cycle,
974 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
975 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
976 "$local_key_file --verbose --verbose",
977 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
978 "$fwknopdCmd $default_server_conf_args $intf_str",
979 'fw_rule_created' => $NEW_RULE_REQUIRED,
980 'fw_rule_removed' => $NEW_RULE_REMOVED,
984 'category' => 'Rijndael SPA',
985 'subcategory' => 'client+server',
986 'detail' => 'complete cycle (udp/53 dns)',
987 'err_msg' => 'could not complete SPA cycle',
988 'function' => \&spa_cycle,
989 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
990 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
991 "$local_key_file --verbose --verbose",
992 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
993 "$fwknopdCmd $default_server_conf_args $intf_str",
994 'fw_rule_created' => $NEW_RULE_REQUIRED,
995 'fw_rule_removed' => $NEW_RULE_REMOVED,
999 'category' => 'Rijndael SPA',
1000 'subcategory' => 'client+server',
1001 'detail' => "-P bpf SPA over port $non_std_spa_port",
1002 'err_msg' => 'could not complete SPA cycle',
1003 'function' => \&spa_cycle,
1004 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1005 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1006 "$fwknopdCmd $default_server_conf_args $intf_str " .
1007 qq|-P "udp port $non_std_spa_port"|,
1008 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1009 'fw_rule_created' => $NEW_RULE_REQUIRED,
1010 'fw_rule_removed' => $NEW_RULE_REMOVED,
1015 'category' => 'Rijndael SPA',
1016 'subcategory' => 'client+server',
1017 'detail' => 'random SPA port (tcp/22 ssh)',
1018 'err_msg' => 'could not complete SPA cycle',
1019 'function' => \&spa_cycle,
1020 'cmdline' => "$default_client_args -r",
1021 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1022 "$fwknopdCmd $default_server_conf_args $intf_str " .
1024 'fw_rule_created' => $NEW_RULE_REQUIRED,
1025 'fw_rule_removed' => $NEW_RULE_REMOVED,
1030 'category' => 'Rijndael SPA',
1031 'subcategory' => 'client+server',
1032 'detail' => 'spoof username (tcp/22)',
1033 'err_msg' => 'could not spoof username',
1034 'function' => \&spoof_username,
1035 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1036 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1037 "$local_key_file --verbose --verbose",
1038 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1039 "$fwknopdCmd $default_server_conf_args $intf_str",
1044 'category' => 'Rijndael SPA',
1045 'subcategory' => 'client+server',
1046 'detail' => 'replay attack detection',
1047 'err_msg' => 'could not detect replay attack',
1048 'function' => \&replay_detection,
1049 'cmdline' => $default_client_args,
1050 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1051 "$fwknopdCmd $default_server_conf_args $intf_str",
1055 'category' => 'Rijndael SPA',
1056 'subcategory' => 'server',
1057 'detail' => 'digest cache structure',
1058 'err_msg' => 'improper digest cache structure',
1059 'function' => \&digest_cache_structure,
1064 'category' => 'Rijndael SPA',
1065 'subcategory' => 'client+server',
1066 'detail' => 'non-base64 altered SPA data',
1067 'err_msg' => 'allowed improper SPA data',
1068 'function' => \&altered_non_base64_spa_data,
1069 'cmdline' => $default_client_args,
1070 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1071 "$fwknopdCmd $default_server_conf_args $intf_str",
1075 'category' => 'Rijndael SPA',
1076 'subcategory' => 'client+server',
1077 'detail' => 'base64 altered SPA data',
1078 'err_msg' => 'allowed improper SPA data',
1079 'function' => \&altered_base64_spa_data,
1080 'cmdline' => $default_client_args,
1081 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1082 "$fwknopdCmd $default_server_conf_args $intf_str",
1086 'category' => 'Rijndael SPA',
1087 'subcategory' => 'client+server',
1088 'detail' => 'appended data to SPA pkt',
1089 'err_msg' => 'allowed improper SPA data',
1090 'function' => \&appended_spa_data,
1091 'cmdline' => $default_client_args,
1092 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1093 "$fwknopdCmd $default_server_conf_args $intf_str",
1097 'category' => 'Rijndael SPA',
1098 'subcategory' => 'client+server',
1099 'detail' => 'prepended data to SPA pkt',
1100 'err_msg' => 'allowed improper SPA data',
1101 'function' => \&prepended_spa_data,
1102 'cmdline' => $default_client_args,
1103 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1104 "$fwknopdCmd $default_server_conf_args $intf_str",
1109 'category' => 'GnuPG (GPG) SPA',
1110 'subcategory' => 'client+server',
1111 'detail' => 'complete cycle (tcp/22 ssh)',
1112 'err_msg' => 'could not complete SPA cycle',
1113 'function' => \&spa_cycle,
1114 'cmdline' => $default_client_gpg_args,
1115 'fwknopd_cmdline' => $default_server_gpg_args,
1116 'fw_rule_created' => $NEW_RULE_REQUIRED,
1117 'fw_rule_removed' => $NEW_RULE_REMOVED,
1121 'category' => 'GnuPG (GPG) SPA',
1122 'subcategory' => 'client+server',
1123 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1124 'err_msg' => 'could not complete SPA cycle',
1125 'function' => \&spa_cycle,
1126 'cmdline' => $default_client_gpg_args,
1127 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1128 "$valgrind_str $fwknopdCmd -c $default_conf " .
1129 "-a $multi_gpg_access_conf $intf_str " .
1130 "-d $default_digest_file -p $default_pid_file",
1131 'fw_rule_created' => $NEW_RULE_REQUIRED,
1132 'fw_rule_removed' => $NEW_RULE_REMOVED,
1137 'category' => 'GnuPG (GPG) SPA',
1138 'subcategory' => 'client+server',
1139 'detail' => 'complete cycle (tcp/23 telnet)',
1140 'err_msg' => 'could not complete SPA cycle',
1141 'function' => \&spa_cycle,
1142 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1143 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1144 "$local_key_file --verbose --verbose " .
1145 "--gpg-recipient-key $gpg_server_key " .
1146 "--gpg-signer-key $gpg_client_key " .
1147 "--gpg-home-dir $gpg_client_home_dir",
1148 'fwknopd_cmdline' => $default_server_gpg_args,
1149 'fw_rule_created' => $NEW_RULE_REQUIRED,
1150 'fw_rule_removed' => $NEW_RULE_REMOVED,
1154 'category' => 'GnuPG (GPG) SPA',
1155 'subcategory' => 'client+server',
1156 'detail' => 'complete cycle (tcp/9418 git)',
1157 'err_msg' => 'could not complete SPA cycle',
1158 'function' => \&spa_cycle,
1159 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1160 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1161 "$local_key_file --verbose --verbose " .
1162 "--gpg-recipient-key $gpg_server_key " .
1163 "--gpg-signer-key $gpg_client_key " .
1164 "--gpg-home-dir $gpg_client_home_dir",
1165 'fwknopd_cmdline' => $default_server_gpg_args,
1166 'fw_rule_created' => $NEW_RULE_REQUIRED,
1167 'fw_rule_removed' => $NEW_RULE_REMOVED,
1171 'category' => 'GnuPG (GPG) SPA',
1172 'subcategory' => 'client+server',
1173 'detail' => 'complete cycle (udp/53 dns)',
1174 'err_msg' => 'could not complete SPA cycle',
1175 'function' => \&spa_cycle,
1176 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1177 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1178 "$local_key_file --verbose --verbose " .
1179 "--gpg-recipient-key $gpg_server_key " .
1180 "--gpg-signer-key $gpg_client_key " .
1181 "--gpg-home-dir $gpg_client_home_dir",
1182 'fwknopd_cmdline' => $default_server_gpg_args,
1183 'fw_rule_created' => $NEW_RULE_REQUIRED,
1184 'fw_rule_removed' => $NEW_RULE_REMOVED,
1189 'category' => 'GnuPG (GPG) SPA',
1190 'subcategory' => 'client+server',
1191 'detail' => 'replay attack detection',
1192 'err_msg' => 'could not detect replay attack',
1193 'function' => \&replay_detection,
1194 'cmdline' => $default_client_gpg_args,
1195 'fwknopd_cmdline' => $default_server_gpg_args,
1200 'category' => 'GnuPG (GPG) SPA',
1201 'subcategory' => 'client+server',
1202 'detail' => 'non-base64 altered SPA data',
1203 'err_msg' => 'allowed improper SPA data',
1204 'function' => \&altered_non_base64_spa_data,
1205 'cmdline' => $default_client_gpg_args,
1206 'fwknopd_cmdline' => $default_server_gpg_args,
1210 'category' => 'GnuPG (GPG) SPA',
1211 'subcategory' => 'client+server',
1212 'detail' => 'base64 altered SPA data',
1213 'err_msg' => 'allowed improper SPA data',
1214 'function' => \&altered_base64_spa_data,
1215 'cmdline' => $default_client_gpg_args,
1216 'fwknopd_cmdline' => $default_server_gpg_args,
1220 'category' => 'GnuPG (GPG) SPA',
1221 'subcategory' => 'client+server',
1222 'detail' => 'appended data to SPA pkt',
1223 'err_msg' => 'allowed improper SPA data',
1224 'function' => \&appended_spa_data,
1225 'cmdline' => $default_client_gpg_args,
1226 'fwknopd_cmdline' => $default_server_gpg_args,
1230 'category' => 'GnuPG (GPG) SPA',
1231 'subcategory' => 'client+server',
1232 'detail' => 'prepended data to SPA pkt',
1233 'err_msg' => 'allowed improper SPA data',
1234 'function' => \&prepended_spa_data,
1235 'cmdline' => $default_client_gpg_args,
1236 'fwknopd_cmdline' => $default_server_gpg_args,
1240 'category' => 'GnuPG (GPG) SPA',
1241 'subcategory' => 'client+server',
1242 'detail' => 'spoof username (tcp/22 ssh)',
1243 'err_msg' => 'could not spoof username',
1244 'function' => \&spoof_username,
1245 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1246 'fwknopd_cmdline' => $default_server_gpg_args,
1250 'category' => 'GnuPG (GPG) SPA',
1251 'subcategory' => 'server',
1252 'detail' => 'digest cache structure',
1253 'err_msg' => 'improper digest cache structure',
1254 'function' => \&digest_cache_structure,
1259 if ($use_valgrind) {
1262 'category' => 'valgrind output',
1263 'subcategory' => 'flagged functions',
1265 'err_msg' => 'could not parse flagged functions',
1266 'function' => \&parse_valgrind_flagged_functions,
1272 'category' => $REQUIRED,
1273 'subcategory' => $OPTIONAL,
1274 'detail' => $REQUIRED,
1275 'function' => $REQUIRED,
1276 'binary' => $OPTIONAL,
1277 'cmdline' => $OPTIONAL,
1278 'fwknopd_cmdline' => $OPTIONAL,
1279 'fatal' => $OPTIONAL,
1280 'exec_err' => $OPTIONAL,
1281 'fw_rule_created' => $OPTIONAL,
1282 'fw_rule_removed' => $OPTIONAL,
1283 'server_conf' => $OPTIONAL,
1284 'positive_output_matches' => $OPTIONAL,
1285 'negative_output_matches' => $OPTIONAL,
1286 'server_positive_output_matches' => $OPTIONAL,
1287 'server_negative_output_matches' => $OPTIONAL,
1291 &diff_test_results();
1295 ### make sure everything looks as expected before continuing
1298 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1299 " args: @args_cp\n\n"
1302 ### save the results from any previous test suite run
1303 ### so that we can potentially compare them with --diff
1304 if ($saved_last_results) {
1305 &logr(" Saved results from previous run " .
1306 "to: ${output_dir}.last/\n\n");
1309 ### main loop through all of the tests
1310 for my $test_hr (@tests) {
1311 &run_test($test_hr);
1314 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1316 copy $logfile, "$output_dir/$logfile" or die $!;
1320 #===================== end main =======================
1323 my $test_hr = shift;
1325 my $msg = "[$test_hr->{'category'}]";
1326 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1327 $msg .= " $test_hr->{'detail'}";
1329 return unless &process_include_exclude($msg);
1339 $current_test_file = "$output_dir/$executed.test";
1340 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1342 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1343 $test_hr->{'msg'} = $msg;
1344 if (&{$test_hr->{'function'}}($test_hr)) {
1345 &logr("pass ($executed)\n");
1348 &logr("fail ($executed)\n");
1351 if ($test_hr->{'fatal'} eq $YES) {
1352 die "[*] required test failed, exiting.";
1359 sub process_include_exclude() {
1362 ### inclusions/exclusions
1363 if (@tests_to_include) {
1365 for my $test (@tests_to_include) {
1366 if ($msg =~ /$test/ or ($use_valgrind
1367 and $msg =~ /valgrind\soutput/)) {
1372 return 0 unless $found;
1374 if (@tests_to_exclude) {
1376 for my $test (@tests_to_exclude) {
1377 if ($msg =~ /$test/) {
1387 sub diff_test_results() {
1389 $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
1390 $diff_dir2 = $output_dir unless $diff_dir2;
1392 die "[*] Need results from a previous run before running --diff"
1393 unless -d $diff_dir2;
1394 die "[*] Current results set does not exist." unless -d $diff_dir1;
1396 my %current_tests = ();
1397 my %previous_tests = ();
1399 ### Only diff results for matching tests (parse the logfile to see which
1400 ### test numbers match across the two test cycles).
1401 &build_results_hash(\%current_tests, $diff_dir1);
1402 &build_results_hash(\%previous_tests, $diff_dir2);
1404 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1405 keys %current_tests) {
1406 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1407 my $current_num = $current_tests{$test_msg}{'num'};
1408 if (defined $previous_tests{$test_msg}) {
1409 print "[+] Checking: $test_msg\n";
1410 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1411 my $previous_num = $previous_tests{$test_msg}{'num'};
1412 if ($current_result ne $previous_result) {
1413 print " DIFF: **$current_result** $test_msg\n";
1416 &diff_results($previous_num, $current_num);
1424 sub diff_results() {
1425 my ($previous_num, $current_num) = @_;
1427 ### edit out any valgrind "==354==" prefixes
1428 my $valgrind_search_re = qr/^==\d+==\s/;
1430 ### remove CMD timestamps
1431 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1433 for my $file ("$diff_dir1/${previous_num}.test",
1434 "$diff_dir1/${previous_num}_fwknopd.test",
1435 "$diff_dir2/${current_num}.test",
1436 "$diff_dir2/${current_num}_fwknopd.test",
1438 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1439 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1442 if (-e "$diff_dir1/${previous_num}.test"
1443 and -e "$diff_dir2/${current_num}.test") {
1444 system "diff -u $diff_dir1/${previous_num}.test " .
1445 "$diff_dir2/${current_num}.test";
1448 if (-e "$diff_dir1/${previous_num}_fwknopd.test"
1449 and -e "$diff_dir2/${current_num}_fwknopd.test") {
1450 system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
1451 "$diff_dir2/${current_num}_fwknopd.test";
1457 sub build_results_hash() {
1458 my ($hr, $dir) = @_;
1460 open F, "< $dir/$logfile" or die $!;
1462 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1463 $hr->{$1}{'pass_fail'} = $2;
1464 $hr->{$1}{'num'} = $3;
1470 sub compile_warnings() {
1472 ### 'make clean' as root
1473 return 0 unless &run_cmd('make -C .. clean',
1474 $cmd_out_tmp, $current_test_file);
1477 my $username = getpwuid((stat($configure_path))[4]);
1478 die "[*] Could not determine $configure_path owner"
1481 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1482 $cmd_out_tmp, $current_test_file);
1486 return 0 unless &run_cmd('make -C ..',
1487 $cmd_out_tmp, $current_test_file);
1491 ### look for compilation warnings - something like:
1492 ### warning: ‘test’ is used uninitialized in this function
1493 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/], $current_test_file);
1495 ### the new binaries should exist
1496 unless (-e $fwknopCmd and -x $fwknopCmd) {
1497 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1498 $current_test_file);
1500 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1501 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1502 $current_test_file);
1508 sub binary_exists() {
1509 my $test_hr = shift;
1510 return 0 unless $test_hr->{'binary'};
1512 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1513 ### libfko.so link on OpenBSD)
1515 if ($test_hr->{'binary'} =~ /libfko/) {
1516 unless (-e $test_hr->{'binary'}) {
1517 for my $file (glob("$lib_dir/libfko.so*")) {
1518 if (-e $file and -x $file) {
1519 $test_hr->{'binary'} = $file;
1520 $libfko_bin = $file;
1527 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1531 sub expected_code_version() {
1532 my $test_hr = shift;
1534 unless (-e '../VERSION') {
1535 &write_test_file("[-] ../VERSION file does not exist.\n",
1536 $current_test_file);
1540 open F, '< ../VERSION' or die $!;
1543 if ($line =~ /(\d.*\d)/) {
1545 return 0 unless &run_cmd($test_hr->{'cmdline'},
1546 $cmd_out_tmp, $current_test_file);
1547 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1552 sub client_send_spa_packet() {
1553 my $test_hr = shift;
1555 &write_key('fwknoptest', $local_key_file);
1557 return 0 unless &run_cmd($test_hr->{'cmdline'},
1558 $cmd_out_tmp, $current_test_file);
1559 return 0 unless &file_find_regex([qr/final\spacked/i],
1560 $current_test_file);
1566 my $test_hr = shift;
1568 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1569 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1571 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1572 $rv = 0 unless $fw_rule_created;
1573 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1574 $rv = 0 if $fw_rule_created;
1577 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1578 $rv = 0 unless $fw_rule_removed;
1579 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1580 $rv = 0 if $fw_rule_removed;
1583 if ($test_hr->{'server_positive_output_matches'}) {
1584 $rv = 0 unless &file_find_regex(
1585 $test_hr->{'server_positive_output_matches'},
1589 if ($test_hr->{'server_negative_output_matches'}) {
1590 $rv = 0 if &file_find_regex(
1591 $test_hr->{'server_negative_output_matches'},
1598 sub spoof_username() {
1599 my $test_hr = shift;
1601 my $rv = &spa_cycle($test_hr);
1603 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1604 $current_test_file)) {
1608 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1609 $server_test_file)) {
1616 sub replay_detection() {
1617 my $test_hr = shift;
1619 ### do a complete SPA cycle and then parse the SPA packet out of the
1620 ### current test file and re-send
1622 return 0 unless &spa_cycle($test_hr);
1624 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1627 &write_test_file("[-] could not get SPA packet " .
1628 "from file: $current_test_file\n",
1629 $current_test_file);
1636 'port' => $default_spa_port,
1637 'dst_ip' => $loopback_ip,
1642 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1643 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1645 $rv = 0 unless $server_was_stopped;
1647 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1648 $server_test_file)) {
1655 sub digest_cache_structure() {
1656 my $test_hr = shift;
1659 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1661 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1663 ### the format should be:
1664 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1665 open F, "< $default_digest_file" or
1666 die "[*] could not open $default_digest_file: $!";
1670 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1671 &write_test_file("[-] invalid digest.cache line: $_",
1672 $current_test_file);
1678 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1679 &write_test_file("[+] DBM digest file format, " .
1680 "assuming this is valid.\n", $current_test_file);
1682 ### don't know what kind of file the digest.cache is
1683 &write_test_file("[-] unrecognized file type for " .
1684 "$default_digest_file.\n", $current_test_file);
1689 &write_test_file("[+] valid digest.cache structure.\n",
1690 $current_test_file);
1696 sub server_bpf_ignore_packet() {
1697 my $test_hr = shift;
1700 my $server_was_stopped = 0;
1701 my $fw_rule_created = 0;
1702 my $fw_rule_removed = 0;
1704 unless (&client_send_spa_packet($test_hr)) {
1705 &write_test_file("[-] fwknop client execution error.\n",
1706 $current_test_file);
1710 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1713 &write_test_file("[-] could not get SPA packet " .
1714 "from file: $current_test_file\n", $current_test_file);
1721 'port' => $default_spa_port,
1722 'dst_ip' => $loopback_ip,
1727 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1728 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1730 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1731 $server_test_file)) {
1738 sub altered_non_base64_spa_data() {
1739 my $test_hr = shift;
1742 my $server_was_stopped = 0;
1743 my $fw_rule_created = 0;
1744 my $fw_rule_removed = 0;
1746 unless (&client_send_spa_packet($test_hr)) {
1747 &write_test_file("[-] fwknop client execution error.\n",
1748 $current_test_file);
1752 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1755 &write_test_file("[-] could not get SPA packet " .
1756 "from file: $current_test_file\n", $current_test_file);
1760 ### alter one byte (change to a ":")
1761 $spa_pkt =~ s|^(.{3}).|$1:|;
1766 'port' => $default_spa_port,
1767 'dst_ip' => $loopback_ip,
1772 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1773 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1775 $rv = 0 unless $server_was_stopped;
1780 sub altered_base64_spa_data() {
1781 my $test_hr = shift;
1784 my $server_was_stopped = 0;
1785 my $fw_rule_created = 0;
1786 my $fw_rule_removed = 0;
1788 unless (&client_send_spa_packet($test_hr)) {
1789 &write_test_file("[-] fwknop client execution error.\n",
1790 $current_test_file);
1794 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1797 &write_test_file("[-] could not get SPA packet " .
1798 "from file: $current_test_file\n", $current_test_file);
1802 $spa_pkt =~ s|^(.{3}).|AAAA|;
1807 'port' => $default_spa_port,
1808 'dst_ip' => $loopback_ip,
1813 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1814 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1816 $rv = 0 unless $server_was_stopped;
1818 if ($fw_rule_created) {
1819 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1822 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1825 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1826 $server_test_file)) {
1833 sub appended_spa_data() {
1834 my $test_hr = shift;
1837 my $server_was_stopped = 0;
1838 my $fw_rule_created = 0;
1839 my $fw_rule_removed = 0;
1841 unless (&client_send_spa_packet($test_hr)) {
1842 &write_test_file("[-] fwknop client execution error.\n",
1843 $current_test_file);
1847 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1850 &write_test_file("[-] could not get SPA packet " .
1851 "from file: $current_test_file\n", $current_test_file);
1860 'port' => $default_spa_port,
1861 'dst_ip' => $loopback_ip,
1866 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1867 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1869 $rv = 0 unless $server_was_stopped;
1871 if ($fw_rule_created) {
1872 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1875 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1878 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1879 $server_test_file)) {
1886 sub prepended_spa_data() {
1887 my $test_hr = shift;
1890 my $server_was_stopped = 0;
1891 my $fw_rule_created = 0;
1892 my $fw_rule_removed = 0;
1894 unless (&client_send_spa_packet($test_hr)) {
1895 &write_test_file("[-] fwknop client execution error.\n",
1896 $current_test_file);
1900 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1903 &write_test_file("[-] could not get SPA packet " .
1904 "from file: $current_test_file\n", $current_test_file);
1908 $spa_pkt = 'AAAA' . $spa_pkt;
1913 'port' => $default_spa_port,
1914 'dst_ip' => $loopback_ip,
1919 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1920 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1922 $rv = 0 unless $server_was_stopped;
1924 if ($fw_rule_created) {
1925 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1928 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1931 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1932 $server_test_file)) {
1939 sub server_start() {
1940 my $test_hr = shift;
1942 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1943 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1945 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1946 $server_test_file)) {
1950 $rv = 0 unless $server_was_stopped;
1956 my $test_hr = shift;
1958 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1959 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1961 $rv = 0 unless $server_was_stopped;
1966 sub server_packet_limit() {
1967 my $test_hr = shift;
1972 'port' => $default_spa_port,
1973 'dst_ip' => $loopback_ip,
1978 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1979 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1981 if (&is_fwknopd_running()) {
1986 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
1987 $server_test_file)) {
1991 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
1992 $server_test_file)) {
1999 sub server_ignore_small_packets() {
2000 my $test_hr = shift;
2005 'port' => $default_spa_port,
2006 'dst_ip' => $loopback_ip,
2007 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2011 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2012 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2016 if (&is_fwknopd_running()) {
2024 sub client_server_interaction() {
2025 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2028 my $server_was_stopped = 1;
2029 my $fw_rule_created = 1;
2030 my $fw_rule_removed = 0;
2032 ### start fwknopd to monitor for the SPA packet over the loopback interface
2033 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2035 ### give fwknopd a chance to parse its config and start sniffing
2036 ### on the loopback interface
2037 if ($use_valgrind) {
2043 ### send the SPA packet(s) to the server either manually using IO::Socket or
2044 ### with the fwknopd client
2045 if ($spa_client_flag == $USE_CLIENT) {
2046 unless (&client_send_spa_packet($test_hr)) {
2047 &write_test_file("[-] fwknop client execution error.\n",
2048 $current_test_file);
2052 &send_packets($pkts_hr);
2055 ### check to see if the SPA packet resulted in a new fw access rule
2057 while (not &is_fw_rule_active($test_hr)) {
2058 &write_test_file("[-] new fw rule does not exist.\n",
2059 $current_test_file);
2065 $fw_rule_created = 0;
2066 $fw_rule_removed = 0;
2069 &time_for_valgrind() if $use_valgrind;
2071 if ($fw_rule_created) {
2072 sleep 3; ### allow time for rule time out.
2073 if (&is_fw_rule_active($test_hr)) {
2074 &write_test_file("[-] new fw rule not timed out.\n",
2075 $current_test_file);
2078 &write_test_file("[+] new fw rule timed out.\n",
2079 $current_test_file);
2080 $fw_rule_removed = 1;
2084 if (&is_fwknopd_running()) {
2086 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2087 $server_test_file)) {
2088 $server_was_stopped = 0;
2091 &write_test_file("[-] server is not running.\n",
2092 $current_test_file);
2093 $server_was_stopped = 0;
2096 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2099 sub get_spa_packet_from_file() {
2104 my $found_trigger_line = 0;
2105 open F, "< $file" or die "[*] Could not open file $file: $!";
2107 if (/final\spacked/i) {
2108 $found_trigger_line = 1;
2111 next unless $found_trigger_line;
2113 ### the next line with non whitespace is the SPA packet
2124 sub send_packets() {
2125 my $pkts_ar = shift;
2127 open F, ">> $current_test_file" or die $!;
2128 print F "[+] send_packets(): Sending the following packets...\n";
2129 print F Dumper $pkts_ar;
2132 for my $pkt_hr (@$pkts_ar) {
2133 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2134 my $socket = IO::Socket::INET->new(
2135 PeerAddr => $pkt_hr->{'dst_ip'},
2136 PeerPort => $pkt_hr->{'port'},
2137 Proto => $pkt_hr->{'proto'},
2139 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2140 "socket to $pkt_hr->{'dst_ip'}: $!";
2142 $socket->send($pkt_hr->{'data'});
2145 } elsif ($pkt_hr->{'proto'} eq 'http') {
2147 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2151 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2156 sub generic_exec() {
2157 my $test_hr = shift;
2161 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2162 $cmd_out_tmp, $current_test_file);
2164 if ($test_hr->{'exec_err'} eq $YES) {
2165 $rv = 0 if $exec_rv;
2167 $rv = 0 unless $exec_rv;
2170 if ($test_hr->{'positive_output_matches'}) {
2171 $rv = 0 unless &file_find_regex(
2172 $test_hr->{'positive_output_matches'},
2173 $current_test_file);
2176 if ($test_hr->{'negative_output_matches'}) {
2177 $rv = 0 if &file_find_regex(
2178 $test_hr->{'negative_output_matches'},
2179 $current_test_file);
2187 my $test_hr = shift;
2188 return 0 unless $test_hr->{'binary'};
2189 &run_cmd("./hardening-check $test_hr->{'binary'}",
2190 $cmd_out_tmp, $current_test_file);
2191 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2192 $current_test_file);
2196 ### check for stack protection
2197 sub stack_protected_binary() {
2198 my $test_hr = shift;
2199 return 0 unless $test_hr->{'binary'};
2200 &run_cmd("./hardening-check $test_hr->{'binary'}",
2201 $cmd_out_tmp, $current_test_file);
2202 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2203 $current_test_file);
2207 ### check for fortified source functions
2208 sub fortify_source_functions() {
2209 my $test_hr = shift;
2210 return 0 unless $test_hr->{'binary'};
2211 &run_cmd("./hardening-check $test_hr->{'binary'}",
2212 $cmd_out_tmp, $current_test_file);
2213 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2214 $current_test_file);
2218 ### check for read-only relocations
2219 sub read_only_relocations() {
2220 my $test_hr = shift;
2221 return 0 unless $test_hr->{'binary'};
2222 &run_cmd("./hardening-check $test_hr->{'binary'}",
2223 $cmd_out_tmp, $current_test_file);
2224 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2225 $current_test_file);
2229 ### check for immediate binding
2230 sub immediate_binding() {
2231 my $test_hr = shift;
2232 return 0 unless $test_hr->{'binary'};
2233 &run_cmd("./hardening-check $test_hr->{'binary'}",
2234 $cmd_out_tmp, $current_test_file);
2235 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2236 $current_test_file);
2242 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2243 "$default_server_conf_args --fw-list-all",
2244 $cmd_out_tmp, $current_test_file);
2252 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2253 'if [ `which iptables` ]; then iptables -V; fi',
2254 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2255 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2256 'if [ `which gpg` ]; then gpg --version; fi',
2257 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2261 'ls -l /usr/lib/*pcap*',
2262 'ls -l /usr/local/lib/*pcap*',
2263 'ls -l /usr/lib/*fko*',
2264 'ls -l /usr/local/lib/*fko*',
2266 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2268 if ($cmd =~ /^ldd/) {
2269 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2273 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2274 ### to enable gpg tests
2275 unless ($have_gpgme == 3) {
2276 push @tests_to_exclude, "GPG";
2282 sub time_for_valgrind() {
2284 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2285 "grep valgrind |grep -v perl | grep -v grep",
2286 $cmd_out_tmp, $current_test_file)) {
2294 sub anonymize_results() {
2296 die "[*] $output_dir does not exist" unless -d $output_dir;
2297 die "[*] $logfile does not exist, has $0 been executed?"
2300 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2303 ### remove non-loopback IP addresses
2304 my $search_re = qr/\b127\.0\.0\.1\b/;
2305 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2306 $search_re = qr/\b127\.0\.0\.2\b/;
2307 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2308 $search_re = qr/\b0\.0\.0\.0\b/;
2309 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2310 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2311 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2312 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2313 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2314 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2316 ### remove hostname from any uname output
2317 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2318 system "perl -p -i -e 'undef \$/; s|$search_re" .
2319 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2321 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2322 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2325 system "tar cvfz $tarfile $logfile $output_dir";
2326 print "[+] Anonymized test results file: $tarfile\n";
2335 my $test_hr = shift;
2337 open F, "> $default_pid_file" or die $!;
2341 &server_start($test_hr);
2343 open F, "< $default_pid_file" or die $!;
2355 sub start_fwknopd() {
2356 my $test_hr = shift;
2358 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2361 die "[*] Could not fork: $!" unless defined $pid;
2365 ### we are the child, so start fwknopd
2366 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2367 $server_cmd_tmp, $server_test_file);
2373 my ($key, $file) = @_;
2375 open K, "> $file" or die "[*] Could not open $file: $!";
2376 print K "$loopback_ip: $key\n";
2377 print K "localhost: $key\n";
2378 print K "some.host.through.proxy.com: $key\n";
2384 open C, ">> $current_test_file"
2385 or die "[*] Could not open $current_test_file: $!";
2386 print C "\n" . localtime() . " [+] PID dump:\n";
2388 &run_cmd("ps auxww | grep knop |grep -v grep",
2389 $cmd_out_tmp, $current_test_file);
2394 my ($cmd, $cmd_out, $file) = @_;
2398 or die "[*] Could not open $file: $!";
2399 print F localtime() . " CMD: $cmd\n";
2403 or die "[*] Could not open $file: $!";
2404 print F localtime() . " CMD: $cmd\n";
2408 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2410 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2411 my @cmd_lines = <C>;
2414 open F, ">> $file" or die "[*] Could not open $file: $!";
2415 print F $_ for @cmd_lines;
2428 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2437 $|++; ### turn off buffering
2439 $< == 0 && $> == 0 or
2440 die "[*] $0: You must be root (or equivalent ",
2441 "UID 0 account) to effectively test fwknop";
2443 ### validate test hashes
2445 for my $test_hr (@tests) {
2446 for my $key (keys %test_keys) {
2447 if ($test_keys{$key} == $REQUIRED) {
2448 die "[*] Missing '$key' element in hash: $hash_num"
2449 unless defined $test_hr->{$key};
2451 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2457 if ($use_valgrind) {
2458 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2459 unless -e $valgrindCmd and -x $valgrindCmd;
2462 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2463 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2465 for my $file ($configure_path,
2468 $default_access_conf,
2469 $no_source_match_access_conf,
2470 $ip_source_match_access_conf,
2471 $subnet_source_match_access_conf,
2472 $no_subnet_source_match_access_conf,
2473 $no_multi_source_match_access_conf,
2474 $multi_source_match_access_conf,
2475 $open_ports_access_conf,
2476 $mismatch_open_ports_access_conf,
2477 $require_user_access_conf,
2478 $mismatch_user_access_conf,
2479 $require_src_access_conf,
2480 $multi_gpg_access_conf,
2481 $multi_stanzas_access_conf,
2482 $expired_access_conf,
2483 $expired_epoch_access_conf,
2484 $future_expired_access_conf,
2485 $invalid_expire_access_conf,
2486 $force_nat_access_conf,
2488 die "[*] $file does not exist" unless -e $file;
2491 if (-d $output_dir) {
2492 if (-d "${output_dir}.last") {
2493 rmtree "${output_dir}.last"
2494 or die "[*] rmtree ${output_dir}.last $!";
2496 mkdir "${output_dir}.last"
2497 or die "[*] ${output_dir}.last: $!";
2498 for my $file (glob("$output_dir/*.test")) {
2499 if ($file =~ m|.*/(.*)|) {
2500 copy $file, "${output_dir}.last/$1" or die $!;
2503 if (-e "$output_dir/init") {
2504 copy "$output_dir/init", "${output_dir}.last/init";
2507 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2509 $saved_last_results = 1;
2511 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2513 unless (-d $run_dir) {
2514 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2517 for my $file (glob("$output_dir/*.test")) {
2518 unlink $file or die "[*] Could not unlink($file)";
2520 if (-e "$output_dir/init") {
2521 unlink "$output_dir/init" or die $!;
2525 unlink $logfile or die $!;
2528 if ($test_include) {
2529 @tests_to_include = split /\s*,\s*/, $test_include;
2531 if ($test_exclude) {
2532 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2535 ### make sure no fwknopd instance is currently running
2536 die "[*] Please stop the running fwknopd instance."
2537 if &is_fwknopd_running();
2539 unless ($enable_recompilation_warnings_check) {
2540 push @tests_to_exclude, 'recompilation';
2543 $sudo_path = &find_command('sudo');
2545 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2546 ### disable compilation checks
2547 push @tests_to_exclude, 'recompilation';
2550 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2553 $platform = 'linux';
2559 unless ($platform eq 'linux') {
2560 push @tests_to_exclude, 'NAT';
2566 sub identify_loopback_intf() {
2567 return if $loopback_intf;
2571 ### lo Link encap:Local Loopback
2572 ### inet addr:127.0.0.1 Mask:255.0.0.0
2573 ### inet6 addr: ::1/128 Scope:Host
2574 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2575 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2576 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2577 ### collisions:0 txqueuelen:0
2578 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2582 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2583 ### options=3<RXCSUM,TXCSUM>
2584 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2585 ### inet6 ::1 prefixlen 128
2586 ### inet 127.0.0.1 netmask 0xff000000
2587 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2590 my $found_loopback_intf = 0;
2592 my $cmd = 'ifconfig -a';
2593 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2595 if (/^(\S+?):?\s+.*loopback/i) {
2599 if (/^\S/ and $intf and not $found_loopback_intf) {
2600 ### should not happen
2603 if ($intf and /\b127\.0\.0\.1\b/) {
2604 $found_loopback_intf = 1;
2610 die "[*] could not determine loopback interface, use --loopback <name>"
2611 unless $found_loopback_intf;
2613 $loopback_intf = $intf;
2618 sub parse_valgrind_flagged_functions() {
2619 for my $file (glob("$output_dir/*.test")) {
2620 my $type = 'server';
2621 $type = 'client' if $file =~ /\d\.test/;
2622 open F, "< $file" or die $!;
2624 ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
2625 if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
2626 $valgrind_flagged_fcns{$type}{"$1 $2"}++;
2627 $valgrind_flagged_fcns_unique{$type}{$1}++;
2633 open F, ">> $current_test_file" or die $!;
2634 for my $type ('client', 'server') {
2635 print F "\n[+] fwknop $type functions (unique view):\n";
2636 next unless defined $valgrind_flagged_fcns_unique{$type};
2637 for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
2638 <=> $valgrind_flagged_fcns_unique{$type}{$a}}
2639 keys %{$valgrind_flagged_fcns_unique{$type}}) {
2640 printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
2642 print F "\n[+] fwknop $type functions (with call line numbers):\n";
2643 for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
2644 <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
2645 printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
2647 next unless defined $valgrind_flagged_fcns{$type};
2654 sub is_fw_rule_active() {
2655 my $test_hr = shift;
2657 my $conf_args = $default_server_conf_args;
2659 if ($test_hr->{'server_conf'}) {
2660 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2661 "-d $default_digest_file -p $default_pid_file";
2664 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2665 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2666 $cmd_out_tmp, $current_test_file);
2670 sub is_fwknopd_running() {
2672 sleep 2 if $use_valgrind;
2674 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2675 "--status", $cmd_out_tmp, $current_test_file);
2677 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2682 sub stop_fwknopd() {
2684 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2685 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2687 if ($use_valgrind) {
2688 &time_for_valgrind();
2696 sub file_find_regex() {
2697 my ($re_ar, $file) = @_;
2700 my @write_lines = ();
2702 open F, "< $file" or die "[*] Could not open $file: $!";
2705 next LINE if $line =~ /file_file_regex\(\)/;
2706 for my $re (@$re_ar) {
2708 push @write_lines, "[.] file_find_regex() " .
2709 "Matched '$re' with line: $line";
2718 for my $line (@write_lines) {
2719 &write_test_file($line, $file);
2722 &write_test_file("[.] find_find_regex() Did not " .
2723 "match any regex in: '@$re_ar'\n", $file);
2729 sub find_command() {
2733 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2735 if (m|^(/.*$cmd)$|) {
2744 sub write_test_file() {
2745 my ($msg, $file) = @_;
2749 or die "[*] Could not open $file: $!";
2754 or die "[*] Could not open $file: $!";
2764 open F, ">> $logfile" or die $!;
2775 -A --Anonymize-results - Prepare anonymized results at:
2777 --diff - Compare the results of one test run to
2778 another. By default this compares output
2779 in ${output_dir}.last to $output_dir
2780 --diff-dir1=<path> - Left hand side of diff directory path,
2781 default is: ${output_dir}.last
2782 --diff-dir2=<path> - Right hand side of diff directory path,
2783 default is: $output_dir
2784 --include=<regex> - Specify a regex to be used over test
2785 names that must match.
2786 --exclude=<regex> - Specify a regex to be used over test
2787 names that must not match.
2788 --enable-recompile - Recompile fwknop sources and look for
2789 compilation warnings.
2790 --enable-valgrind - Run every test underneath valgrind.
2791 --List - List test names.
2792 --loopback-intf=<intf> - Specify loopback interface name (default
2793 depends on the OS where the test suite
2795 --output-dir=<path> - Path to output directory, default is:
2797 --fwknop-path=<path> - Path to fwknop binary, default is:
2799 --fwknopd-path=<path> - Path to fwknopd binary, default is:
2801 --libfko-path=<path> - Path to libfko, default is:
2803 --valgrind-path=<path> - Path to valgrind, default is:
2805 -h --help - Display usage on STDOUT and exit.