8 use Getopt::Long 'GetOptions';
11 #==================== config =====================
12 my $logfile = 'test.log';
13 my $local_key_file = 'local_spa.key';
14 my $output_dir = 'output';
15 my $lib_dir = '../lib/.libs';
16 my $conf_dir = 'conf';
18 my $configure_path = '../configure';
19 my $cmd_out_tmp = 'cmd.out';
20 my $server_cmd_tmp = 'server_cmd.out';
21 my $gpg_client_home_dir = "$conf_dir/client-gpg";
23 my $nat_conf = "$conf_dir/nat_fwknopd.conf";
24 my $default_conf = "$conf_dir/default_fwknopd.conf";
25 my $default_access_conf = "$conf_dir/default_access.conf";
26 my $ecb_mode_access_conf = "$conf_dir/ecb_mode_access.conf";
27 my $ctr_mode_access_conf = "$conf_dir/ctr_mode_access.conf";
28 my $cfb_mode_access_conf = "$conf_dir/cfb_mode_access.conf";
29 my $ofb_mode_access_conf = "$conf_dir/ofb_mode_access.conf";
30 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
31 my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
32 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
33 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
34 my $invalid_source_access_conf = "$conf_dir/invalid_source_access.conf";
35 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
36 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
37 my $default_digest_file = "$run_dir/digest.cache";
38 my $default_pid_file = "$run_dir/fwknopd.pid";
39 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
40 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
41 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
42 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
43 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
44 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
45 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
46 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
47 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
48 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
49 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
50 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
51 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
52 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
54 my $fwknopCmd = '../client/.libs/fwknop';
55 my $fwknopdCmd = '../server/.libs/fwknopd';
56 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
57 my $valgrindCmd = '/usr/bin/valgrind';
59 my $gpg_server_key = '361BBAD4';
60 my $gpg_client_key = '6A3FAD56';
62 my $loopback_ip = '127.0.0.1';
63 my $fake_ip = '127.0.0.2';
64 my $internal_nat_host = '192.168.1.2';
65 my $force_nat_host = '192.168.1.123';
66 my $default_spa_port = 62201;
67 my $non_std_spa_port = 12345;
69 my $spoof_user = 'testuser';
70 #================== end config ===================
75 my $test_include = '';
76 my @tests_to_include = ();
77 my $test_exclude = '';
78 my @tests_to_exclude = ();
80 my $loopback_intf = '';
81 my $anonymize_results = 0;
82 my $current_test_file = "$output_dir/init";
83 my $tarfile = 'test_fwknop.tar.gz';
84 my $server_test_file = '';
86 my $valgrind_str = '';
87 my $saved_last_results = 0;
89 my $enable_recompilation_warnings_check = 0;
90 my $enable_profile_coverage_check = 0;
98 my $USE_PREDEF_PKTS = 1;
102 my $NEW_RULE_REQUIRED = 1;
103 my $REQUIRE_NO_NEW_RULE = 2;
104 my $NEW_RULE_REMOVED = 1;
105 my $REQUIRE_NO_NEW_REMOVED = 2;
107 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
111 exit 1 unless GetOptions(
112 'Anonymize-results' => \$anonymize_results,
113 'fwknop-path=s' => \$fwknopCmd,
114 'fwknopd-path=s' => \$fwknopdCmd,
115 'libfko-path=s' => \$libfko_bin,
116 'loopback-intf=s' => \$loopback_intf,
117 'test-include=s' => \$test_include,
118 'include=s' => \$test_include, ### synonym
119 'test-exclude=s' => \$test_exclude,
120 'exclude=s' => \$test_exclude, ### synonym
121 'enable-recompile-check' => \$enable_recompilation_warnings_check,
122 'enable-profile-coverage-check' => \$enable_profile_coverage_check,
123 'List-mode' => \$list_mode,
124 'enable-valgrind' => \$use_valgrind,
125 'valgrind-path=s' => \$valgrindCmd,
126 'diff' => \$diff_mode,
132 ### create an anonymized tar file of test suite results that can be
133 ### emailed around to assist in debugging fwknop communications
134 exit &anonymize_results() if $anonymize_results;
136 &identify_loopback_intf();
138 $valgrind_str = "$valgrindCmd --leak-check=full " .
139 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
141 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
143 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
144 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
145 "$local_key_file --verbose --verbose";
147 my $default_client_gpg_args = "$default_client_args " .
148 "--gpg-recipient-key $gpg_server_key " .
149 "--gpg-signer-key $gpg_client_key " .
150 "--gpg-home-dir $gpg_client_home_dir";
152 my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
153 "-d $default_digest_file -p $default_pid_file";
155 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
156 "$valgrind_str $fwknopdCmd -c $default_conf " .
157 "-a $gpg_access_conf $intf_str " .
158 "-d $default_digest_file -p $default_pid_file";
160 ### point the compiled binaries at the local libary path
161 ### instead of any installed libfko instance
162 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
164 ### main array that defines the tests we will run
167 'category' => 'recompilation',
168 'detail' => 'recompile and look for compilation warnings',
169 'err_msg' => 'compile warnings exist',
170 'function' => \&compile_warnings,
174 'category' => 'build',
175 'subcategory' => 'client',
176 'detail' => 'binary exists',
177 'err_msg' => 'binary not found',
178 'function' => \&binary_exists,
179 'binary' => $fwknopCmd,
183 'category' => 'build security',
184 'subcategory' => 'client',
185 'detail' => 'Position Independent Executable (PIE)',
186 'err_msg' => 'non PIE binary (fwknop client)',
187 'function' => \&pie_binary,
188 'binary' => $fwknopCmd,
192 'category' => 'build security',
193 'subcategory' => 'client',
194 'detail' => 'stack protected binary',
195 'err_msg' => 'non stack protected binary (fwknop client)',
196 'function' => \&stack_protected_binary,
197 'binary' => $fwknopCmd,
201 'category' => 'build security',
202 'subcategory' => 'client',
203 'detail' => 'fortify source functions',
204 'err_msg' => 'source functions not fortified (fwknop client)',
205 'function' => \&fortify_source_functions,
206 'binary' => $fwknopCmd,
210 'category' => 'build security',
211 'subcategory' => 'client',
212 'detail' => 'read-only relocations',
213 'err_msg' => 'no read-only relocations (fwknop client)',
214 'function' => \&read_only_relocations,
215 'binary' => $fwknopCmd,
219 'category' => 'build security',
220 'subcategory' => 'client',
221 'detail' => 'immediate binding',
222 'err_msg' => 'no immediate binding (fwknop client)',
223 'function' => \&immediate_binding,
224 'binary' => $fwknopCmd,
229 'category' => 'build',
230 'subcategory' => 'server',
231 'detail' => 'binary exists',
232 'err_msg' => 'binary not found',
233 'function' => \&binary_exists,
234 'binary' => $fwknopdCmd,
239 'category' => 'build security',
240 'subcategory' => 'server',
241 'detail' => 'Position Independent Executable (PIE)',
242 'err_msg' => 'non PIE binary (fwknopd server)',
243 'function' => \&pie_binary,
244 'binary' => $fwknopdCmd,
248 'category' => 'build security',
249 'subcategory' => 'server',
250 'detail' => 'stack protected binary',
251 'err_msg' => 'non stack protected binary (fwknopd server)',
252 'function' => \&stack_protected_binary,
253 'binary' => $fwknopdCmd,
257 'category' => 'build security',
258 'subcategory' => 'server',
259 'detail' => 'fortify source functions',
260 'err_msg' => 'source functions not fortified (fwknopd server)',
261 'function' => \&fortify_source_functions,
262 'binary' => $fwknopdCmd,
266 'category' => 'build security',
267 'subcategory' => 'server',
268 'detail' => 'read-only relocations',
269 'err_msg' => 'no read-only relocations (fwknopd server)',
270 'function' => \&read_only_relocations,
271 'binary' => $fwknopdCmd,
275 'category' => 'build security',
276 'subcategory' => 'server',
277 'detail' => 'immediate binding',
278 'err_msg' => 'no immediate binding (fwknopd server)',
279 'function' => \&immediate_binding,
280 'binary' => $fwknopdCmd,
285 'category' => 'build',
286 'subcategory' => 'libfko',
287 'detail' => 'binary exists',
288 'err_msg' => 'binary not found',
289 'function' => \&binary_exists,
290 'binary' => $libfko_bin,
294 'category' => 'build security',
295 'subcategory' => 'libfko',
296 'detail' => 'stack protected binary',
297 'err_msg' => 'non stack protected binary (libfko)',
298 'function' => \&stack_protected_binary,
299 'binary' => $libfko_bin,
303 'category' => 'build security',
304 'subcategory' => 'libfko',
305 'detail' => 'fortify source functions',
306 'err_msg' => 'source functions not fortified (libfko)',
307 'function' => \&fortify_source_functions,
308 'binary' => $libfko_bin,
312 'category' => 'build security',
313 'subcategory' => 'libfko',
314 'detail' => 'read-only relocations',
315 'err_msg' => 'no read-only relocations (libfko)',
316 'function' => \&read_only_relocations,
317 'binary' => $libfko_bin,
321 'category' => 'build security',
322 'subcategory' => 'libfko',
323 'detail' => 'immediate binding',
324 'err_msg' => 'no immediate binding (libfko)',
325 'function' => \&immediate_binding,
326 'binary' => $libfko_bin,
331 'category' => 'preliminaries',
332 'subcategory' => 'client',
333 'detail' => 'usage info',
334 'err_msg' => 'could not get usage info',
335 'function' => \&generic_exec,
336 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
340 'category' => 'preliminaries',
341 'subcategory' => 'client',
342 'detail' => 'getopt() no such argument',
343 'err_msg' => 'getopt() allowed non-existant argument',
344 'function' => \&generic_exec,
345 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
350 'category' => 'preliminaries',
351 'subcategory' => 'client',
352 'detail' => '--test mode, packet not sent',
353 'err_msg' => '--test mode, packet sent?',
354 'function' => \&generic_exec,
355 'positive_output_matches' => [qr/test\smode\senabled/],
356 'cmdline' => "$default_client_args --test",
361 'category' => 'preliminaries',
362 'subcategory' => 'client',
363 'detail' => 'expected code version',
364 'err_msg' => 'code version mis-match',
365 'function' => \&expected_code_version,
366 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
371 'category' => 'preliminaries',
372 'subcategory' => 'server',
373 'detail' => 'usage info',
374 'err_msg' => 'could not get usage info',
375 'function' => \&generic_exec,
376 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
380 'category' => 'preliminaries',
381 'subcategory' => 'server',
382 'detail' => 'getopt() no such argument',
383 'err_msg' => 'getopt() allowed non-existant argument',
384 'function' => \&generic_exec,
385 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
391 'category' => 'preliminaries',
392 'subcategory' => 'server',
393 'detail' => 'expected code version',
394 'err_msg' => 'code version mis-match',
395 'function' => \&expected_code_version,
396 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
397 "$fwknopdCmd -c $default_conf -a " .
398 "$default_access_conf --version",
402 'category' => 'preliminaries',
403 'detail' => 'collecting system specifics',
404 'err_msg' => 'could not get complete system specs',
405 'function' => \&specs,
406 'binary' => $fwknopdCmd,
411 'category' => 'basic operations',
412 'detail' => 'dump config',
413 'err_msg' => 'could not dump configuration',
414 'function' => \&generic_exec,
415 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
417 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
418 "$fwknopdCmd -c $default_conf " .
419 "-a $default_access_conf --dump-config",
423 'category' => 'basic operations',
424 'detail' => 'override config',
425 'err_msg' => 'could not override configuration',
426 'function' => \&generic_exec,
427 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
429 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
430 "$fwknopdCmd $default_server_conf_args " .
431 "-O $conf_dir/override_fwknopd.conf --dump-config",
436 'category' => 'basic operations',
437 'subcategory' => 'client',
438 'detail' => '--get-key path validation',
439 'err_msg' => 'accepted improper --get-key path',
440 'function' => \&generic_exec,
441 'positive_output_matches' => [qr/could\snot\sopen/i],
443 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
444 "$fwknopCmd -A tcp/22 -s $fake_ip " .
445 "-D $loopback_ip --get-key not/there",
449 'category' => 'basic operations',
450 'subcategory' => 'client',
451 'detail' => 'require [-s|-R|-a]',
452 'err_msg' => 'allowed null allow IP',
453 'function' => \&generic_exec,
454 'positive_output_matches' => [qr/must\suse\sone\sof/i],
456 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
457 "$fwknopCmd -D $loopback_ip",
461 'category' => 'basic operations',
462 'subcategory' => 'client',
463 'detail' => '--allow-ip <IP> valid IP',
464 'err_msg' => 'permitted invalid --allow-ip arg',
465 'function' => \&generic_exec,
466 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
468 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
469 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
473 'category' => 'basic operations',
474 'subcategory' => 'client',
475 'detail' => '-A <proto>/<port> specification',
476 'err_msg' => 'permitted invalid -A <proto>/<port>',
477 'function' => \&generic_exec,
478 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
480 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
481 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
485 'category' => 'basic operations',
486 'subcategory' => 'client',
487 'detail' => 'generate SPA packet',
488 'err_msg' => 'could not generate SPA packet',
489 'function' => \&client_send_spa_packet,
490 'cmdline' => $default_client_args,
495 'category' => 'basic operations',
496 'subcategory' => 'server',
497 'detail' => 'list current fwknopd fw rules',
498 'err_msg' => 'could not list current fwknopd fw rules',
499 'function' => \&generic_exec,
500 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
501 "$fwknopdCmd $default_server_conf_args --fw-list",
505 'category' => 'basic operations',
506 'subcategory' => 'server',
507 'detail' => 'list all current fw rules',
508 'err_msg' => 'could not list all current fw rules',
509 'function' => \&generic_exec,
510 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
511 "$fwknopdCmd $default_server_conf_args --fw-list-all",
515 'category' => 'basic operations',
516 'subcategory' => 'server',
517 'detail' => 'flush current firewall rules',
518 'err_msg' => 'could not flush current fw rules',
519 'function' => \&generic_exec,
520 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
521 "$fwknopdCmd $default_server_conf_args --fw-flush",
526 'category' => 'basic operations',
527 'subcategory' => 'server',
529 'err_msg' => 'start error',
530 'function' => \&server_start,
531 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
532 "$fwknopdCmd $default_server_conf_args $intf_str",
536 'category' => 'basic operations',
537 'subcategory' => 'server',
539 'err_msg' => 'stop error',
540 'function' => \&server_stop,
541 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
542 "$fwknopdCmd $default_server_conf_args $intf_str",
546 'category' => 'basic operations',
547 'subcategory' => 'server',
548 'detail' => 'write PID',
549 'err_msg' => 'did not write PID',
550 'function' => \&write_pid,
551 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
552 "$fwknopdCmd $default_server_conf_args $intf_str",
557 'category' => 'basic operations',
558 'subcategory' => 'server',
559 'detail' => '--packet-limit 1 exit',
560 'err_msg' => 'did not exit after one packet',
561 'function' => \&server_packet_limit,
562 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
563 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
567 'category' => 'basic operations',
568 'subcategory' => 'server',
569 'detail' => 'ignore packets < min SPA len (140)',
570 'err_msg' => 'did not ignore small packets',
571 'function' => \&server_ignore_small_packets,
572 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
573 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
577 'category' => 'basic operations',
578 'subcategory' => 'server',
579 'detail' => '-P bpf filter ignore packet',
580 'err_msg' => 'filter did not ignore packet',
581 'function' => \&server_bpf_ignore_packet,
582 'cmdline' => $default_client_args,
583 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
584 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
585 qq|-P "udp port $non_std_spa_port"|,
590 'category' => 'Rijndael SPA',
591 'subcategory' => 'client+server',
592 'detail' => 'complete cycle (tcp/22 ssh)',
593 'err_msg' => 'could not complete SPA cycle',
594 'function' => \&spa_cycle,
595 'cmdline' => $default_client_args,
596 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
597 "$fwknopdCmd $default_server_conf_args $intf_str",
598 'fw_rule_created' => $NEW_RULE_REQUIRED,
599 'fw_rule_removed' => $NEW_RULE_REMOVED,
603 'category' => 'Rijndael SPA',
604 'subcategory' => 'client+server',
605 'detail' => 'packet aging (past) (tcp/22 ssh)',
606 'err_msg' => 'old SPA packet accepted',
607 'function' => \&spa_cycle,
608 'cmdline' => "$default_client_args --time-offset-minus 300s",
609 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
610 "$fwknopdCmd $default_server_conf_args $intf_str",
611 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
612 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
616 'category' => 'Rijndael SPA',
617 'subcategory' => 'client+server',
618 'detail' => 'packet aging (future) (tcp/22 ssh)',
619 'err_msg' => 'future SPA packet accepted',
620 'function' => \&spa_cycle,
621 'cmdline' => "$default_client_args --time-offset-plus 300s",
622 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
623 "$fwknopdCmd $default_server_conf_args $intf_str",
624 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
625 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
629 'category' => 'Rijndael SPA',
630 'subcategory' => 'client+server',
631 'detail' => 'invalid SOURCE (tcp/22 ssh)',
632 'err_msg' => 'SPA packet accepted',
633 'function' => \&spa_cycle,
634 'cmdline' => $default_client_args,
635 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
636 "$fwknopdCmd -c $default_conf -a $invalid_source_access_conf " .
637 "-d $default_digest_file -p $default_pid_file $intf_str",
638 'server_positive_output_matches' => [qr/Fatal\serror\sparsing\sIP\sto\sint/],
639 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
643 'category' => 'Rijndael SPA',
644 'subcategory' => 'client+server',
645 'detail' => 'expired stanza (tcp/22 ssh)',
646 'err_msg' => 'SPA packet accepted',
647 'function' => \&spa_cycle,
648 'cmdline' => $default_client_args,
649 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
650 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
651 "-d $default_digest_file -p $default_pid_file $intf_str",
652 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
653 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
657 'category' => 'Rijndael SPA',
658 'subcategory' => 'client+server',
659 'detail' => 'invalid expire date (tcp/22 ssh)',
660 'err_msg' => 'SPA packet accepted',
661 'function' => \&spa_cycle,
662 'cmdline' => $default_client_args,
663 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
664 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
665 "-d $default_digest_file -p $default_pid_file $intf_str",
666 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
667 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
671 'category' => 'Rijndael SPA',
672 'subcategory' => 'client+server',
673 'detail' => 'expired epoch stanza (tcp/22 ssh)',
674 'err_msg' => 'SPA packet accepted',
675 'function' => \&spa_cycle,
676 'cmdline' => $default_client_args,
677 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
678 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
679 "-d $default_digest_file -p $default_pid_file $intf_str",
680 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
681 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
685 'category' => 'Rijndael SPA',
686 'subcategory' => 'client+server',
687 'detail' => 'future expired stanza (tcp/22 ssh)',
688 'err_msg' => 'SPA packet not accepted',
689 'function' => \&spa_cycle,
690 'cmdline' => $default_client_args,
691 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
692 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
693 "-d $default_digest_file -p $default_pid_file $intf_str",
694 'fw_rule_created' => $NEW_RULE_REQUIRED,
695 'fw_rule_removed' => $NEW_RULE_REMOVED,
700 'category' => 'Rijndael SPA',
701 'subcategory' => 'client+server',
702 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
703 'err_msg' => "improper OPEN_PORTS result",
704 'function' => \&spa_cycle,
705 'cmdline' => $default_client_args,
706 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
707 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
708 "-d $default_digest_file -p $default_pid_file $intf_str",
709 'fw_rule_created' => $NEW_RULE_REQUIRED,
710 'fw_rule_removed' => $NEW_RULE_REMOVED,
714 'category' => 'Rijndael SPA',
715 'subcategory' => 'client+server',
716 'detail' => 'OPEN_PORTS mismatch',
717 'err_msg' => "SPA packet accepted",
718 'function' => \&spa_cycle,
719 'cmdline' => $default_client_args,
720 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
721 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
722 "-d $default_digest_file -p $default_pid_file $intf_str",
723 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
724 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
728 'category' => 'Rijndael SPA',
729 'subcategory' => 'client+server',
730 'detail' => 'require user (tcp/22 ssh)',
731 'err_msg' => "missed require user criteria",
732 'function' => \&spa_cycle,
733 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
734 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
735 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
736 "-d $default_digest_file -p $default_pid_file $intf_str",
737 'fw_rule_created' => $NEW_RULE_REQUIRED,
738 'fw_rule_removed' => $NEW_RULE_REMOVED,
742 'category' => 'Rijndael SPA',
743 'subcategory' => 'client+server',
744 'detail' => 'user mismatch (tcp/22 ssh)',
745 'err_msg' => "improper user accepted for access",
746 'function' => \&user_mismatch,
747 'function' => \&spa_cycle,
748 'cmdline' => $default_client_args,
749 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
750 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
751 "-d $default_digest_file -p $default_pid_file $intf_str",
752 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
753 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
757 'category' => 'Rijndael SPA',
758 'subcategory' => 'client+server',
759 'detail' => 'require src (tcp/22 ssh)',
760 'err_msg' => "fw rule not created",
761 'function' => \&spa_cycle,
762 'cmdline' => $default_client_args,
763 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
764 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
765 "-d $default_digest_file -p $default_pid_file $intf_str",
766 'fw_rule_created' => $NEW_RULE_REQUIRED,
767 'fw_rule_removed' => $NEW_RULE_REMOVED,
771 'category' => 'Rijndael SPA',
772 'subcategory' => 'client+server',
773 'detail' => 'mismatch require src (tcp/22 ssh)',
774 'err_msg' => "fw rule created",
775 'function' => \&spa_cycle,
776 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
777 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
778 "$local_key_file --verbose --verbose",
779 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
780 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
781 "-d $default_digest_file -p $default_pid_file $intf_str",
782 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
783 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
788 'category' => 'Rijndael SPA',
789 'subcategory' => 'client+server',
790 'detail' => 'IP filtering (tcp/22 ssh)',
791 'err_msg' => "did not filter $loopback_ip",
792 'function' => \&spa_cycle,
793 'cmdline' => $default_client_args,
794 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
795 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
796 "-d $default_digest_file -p $default_pid_file $intf_str",
797 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
798 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
802 'category' => 'Rijndael SPA',
803 'subcategory' => 'client+server',
804 'detail' => 'subnet filtering (tcp/22 ssh)',
805 'err_msg' => "did not filter $loopback_ip",
806 'function' => \&spa_cycle,
807 'cmdline' => $default_client_args,
808 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
809 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
810 "-d $default_digest_file -p $default_pid_file $intf_str",
811 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
812 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
816 'category' => 'Rijndael SPA',
817 'subcategory' => 'client+server',
818 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
819 'err_msg' => "did not filter $loopback_ip",
820 'function' => \&spa_cycle,
821 'cmdline' => $default_client_args,
822 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
823 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
824 "-d $default_digest_file -p $default_pid_file $intf_str",
825 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
826 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
830 'category' => 'Rijndael SPA',
831 'subcategory' => 'client+server',
832 'detail' => 'IP match (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 $default_conf -a $ip_source_match_access_conf " .
838 "-d $default_digest_file -p $default_pid_file $intf_str",
839 'fw_rule_created' => $NEW_RULE_REQUIRED,
840 'fw_rule_removed' => $NEW_RULE_REMOVED,
844 'category' => 'Rijndael SPA',
845 'subcategory' => 'client+server',
846 'detail' => 'subnet match (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 $default_conf -a $subnet_source_match_access_conf " .
852 "-d $default_digest_file -p $default_pid_file $intf_str",
853 'fw_rule_created' => $NEW_RULE_REQUIRED,
854 'fw_rule_removed' => $NEW_RULE_REMOVED,
858 'category' => 'Rijndael SPA',
859 'subcategory' => 'client+server',
860 'detail' => 'multi IP/net match (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 $default_conf -a $multi_source_match_access_conf " .
866 "-d $default_digest_file -p $default_pid_file $intf_str",
867 'fw_rule_created' => $NEW_RULE_REQUIRED,
868 'fw_rule_removed' => $NEW_RULE_REMOVED,
872 'category' => 'Rijndael SPA',
873 'subcategory' => 'client+server',
874 'detail' => 'multi access stanzas (tcp/22 ssh)',
875 'err_msg' => "could not complete SPA cycle",
876 'function' => \&spa_cycle,
877 'cmdline' => $default_client_args,
878 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
879 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
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' => 'bad/good key stanzas (tcp/22 ssh)',
889 'err_msg' => "could not complete SPA cycle",
890 'function' => \&spa_cycle,
891 'cmdline' => $default_client_args,
892 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
893 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
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,
901 'category' => 'Rijndael SPA',
902 'subcategory' => 'client+server',
903 'detail' => "non-enabled NAT (tcp/22 ssh)",
904 'err_msg' => "SPA packet not filtered",
905 'function' => \&spa_cycle,
906 'cmdline' => "$default_client_args -N $internal_nat_host:22",
907 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
908 "$fwknopdCmd $default_server_conf_args $intf_str",
909 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
910 'server_conf' => $nat_conf,
911 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
915 'category' => 'Rijndael SPA',
916 'subcategory' => 'client+server',
917 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
918 'err_msg' => "could not complete NAT SPA cycle",
919 'function' => \&spa_cycle,
920 'cmdline' => "$default_client_args -N $internal_nat_host:22",
921 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
922 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
923 "-d $default_digest_file -p $default_pid_file $intf_str",
924 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
925 'fw_rule_created' => $NEW_RULE_REQUIRED,
926 'fw_rule_removed' => $NEW_RULE_REMOVED,
927 'server_conf' => $nat_conf,
932 'category' => 'Rijndael SPA',
933 'subcategory' => 'client+server',
934 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
935 'err_msg' => "could not complete NAT SPA cycle",
936 'function' => \&spa_cycle,
937 'cmdline' => $default_client_args,
938 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
939 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
940 "-d $default_digest_file -p $default_pid_file $intf_str",
941 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
942 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
943 'fw_rule_created' => $NEW_RULE_REQUIRED,
944 'fw_rule_removed' => $NEW_RULE_REMOVED,
945 'server_conf' => $nat_conf,
949 'category' => 'Rijndael SPA',
950 'subcategory' => 'client+server',
951 'detail' => 'ECB mode (tcp/22 ssh)',
952 'err_msg' => 'could not complete SPA cycle',
953 'function' => \&spa_cycle,
954 'cmdline' => "$default_client_args -M ecb",
955 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
956 "$fwknopdCmd -c $default_conf -a $ecb_mode_access_conf " .
957 "-d $default_digest_file -p $default_pid_file $intf_str",
958 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
959 'fw_rule_created' => $NEW_RULE_REQUIRED,
960 'fw_rule_removed' => $NEW_RULE_REMOVED,
964 'category' => 'Rijndael SPA',
965 'subcategory' => 'client+server',
966 'detail' => 'CFB mode (tcp/22 ssh)',
967 'err_msg' => 'could not complete SPA cycle',
968 'function' => \&spa_cycle,
969 'cmdline' => "$default_client_args -M cfb",
970 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
971 "$fwknopdCmd -c $default_conf -a $cfb_mode_access_conf " .
972 "-d $default_digest_file -p $default_pid_file $intf_str",
973 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
974 'fw_rule_created' => $NEW_RULE_REQUIRED,
975 'fw_rule_removed' => $NEW_RULE_REMOVED,
979 'category' => 'Rijndael SPA',
980 'subcategory' => 'client+server',
981 'detail' => 'CTR mode (tcp/22 ssh)',
982 'err_msg' => 'could not complete SPA cycle',
983 'function' => \&spa_cycle,
984 'cmdline' => "$default_client_args -M ctr",
985 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
986 "$fwknopdCmd -c $default_conf -a $ctr_mode_access_conf " .
987 "-d $default_digest_file -p $default_pid_file $intf_str",
988 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
989 'fw_rule_created' => $NEW_RULE_REQUIRED,
990 'fw_rule_removed' => $NEW_RULE_REMOVED,
994 'category' => 'Rijndael SPA',
995 'subcategory' => 'client+server',
996 'detail' => 'OFB mode (tcp/22 ssh)',
997 'err_msg' => 'could not complete SPA cycle',
998 'function' => \&spa_cycle,
999 'cmdline' => "$default_client_args -M ofb",
1000 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1001 "$fwknopdCmd -c $default_conf -a $ofb_mode_access_conf " .
1002 "-d $default_digest_file -p $default_pid_file $intf_str",
1003 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
1004 'fw_rule_created' => $NEW_RULE_REQUIRED,
1005 'fw_rule_removed' => $NEW_RULE_REMOVED,
1010 'category' => 'Rijndael SPA',
1011 'subcategory' => 'client+server',
1012 'detail' => 'mode mismatch (tcp/22 ssh)',
1013 'err_msg' => 'server accepted mismatch enc mode',
1014 'function' => \&spa_cycle,
1015 'cmdline' => "$default_client_args -M ecb",
1016 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1017 "$fwknopdCmd -c $default_conf -a $default_access_conf " .
1018 "-d $default_digest_file -p $default_pid_file $intf_str",
1019 'server_positive_output_matches' => [qr/Decryption\sfailed/i],
1020 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
1025 'category' => 'Rijndael SPA',
1026 'subcategory' => 'client+server',
1027 'detail' => 'complete cycle (tcp/23 telnet)',
1028 'err_msg' => 'could not complete SPA cycle',
1029 'function' => \&spa_cycle,
1030 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1031 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1032 "$local_key_file --verbose --verbose",
1033 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1034 "$fwknopdCmd $default_server_conf_args $intf_str",
1035 'fw_rule_created' => $NEW_RULE_REQUIRED,
1036 'fw_rule_removed' => $NEW_RULE_REMOVED,
1040 'category' => 'Rijndael SPA',
1041 'subcategory' => 'client+server',
1042 'detail' => 'complete cycle (tcp/9418 git)',
1043 'err_msg' => 'could not complete SPA cycle',
1044 'function' => \&spa_cycle,
1045 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1046 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1047 "$local_key_file --verbose --verbose",
1048 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1049 "$fwknopdCmd $default_server_conf_args $intf_str",
1050 'fw_rule_created' => $NEW_RULE_REQUIRED,
1051 'fw_rule_removed' => $NEW_RULE_REMOVED,
1055 'category' => 'Rijndael SPA',
1056 'subcategory' => 'client+server',
1057 'detail' => 'complete cycle (udp/53 dns)',
1058 'err_msg' => 'could not complete SPA cycle',
1059 'function' => \&spa_cycle,
1060 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1061 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1062 "$local_key_file --verbose --verbose",
1063 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1064 "$fwknopdCmd $default_server_conf_args $intf_str",
1065 'fw_rule_created' => $NEW_RULE_REQUIRED,
1066 'fw_rule_removed' => $NEW_RULE_REMOVED,
1070 'category' => 'Rijndael SPA',
1071 'subcategory' => 'client+server',
1072 'detail' => "-P bpf SPA over port $non_std_spa_port",
1073 'err_msg' => 'could not complete SPA cycle',
1074 'function' => \&spa_cycle,
1075 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1076 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1077 "$fwknopdCmd $default_server_conf_args $intf_str " .
1078 qq|-P "udp port $non_std_spa_port"|,
1079 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1080 'fw_rule_created' => $NEW_RULE_REQUIRED,
1081 'fw_rule_removed' => $NEW_RULE_REMOVED,
1086 'category' => 'Rijndael SPA',
1087 'subcategory' => 'client+server',
1088 'detail' => 'random SPA port (tcp/22 ssh)',
1089 'err_msg' => 'could not complete SPA cycle',
1090 'function' => \&spa_cycle,
1091 'cmdline' => "$default_client_args -r",
1092 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1093 "$fwknopdCmd $default_server_conf_args $intf_str " .
1095 'fw_rule_created' => $NEW_RULE_REQUIRED,
1096 'fw_rule_removed' => $NEW_RULE_REMOVED,
1101 'category' => 'Rijndael SPA',
1102 'subcategory' => 'client+server',
1103 'detail' => 'spoof username (tcp/22)',
1104 'err_msg' => 'could not spoof username',
1105 'function' => \&spoof_username,
1106 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1107 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1108 "$local_key_file --verbose --verbose",
1109 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1110 "$fwknopdCmd $default_server_conf_args $intf_str",
1115 'category' => 'Rijndael SPA',
1116 'subcategory' => 'client+server',
1117 'detail' => 'replay attack detection',
1118 'err_msg' => 'could not detect replay attack',
1119 'function' => \&replay_detection,
1120 'cmdline' => $default_client_args,
1121 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1122 "$fwknopdCmd $default_server_conf_args $intf_str",
1126 'category' => 'Rijndael SPA',
1127 'subcategory' => 'server',
1128 'detail' => 'digest cache structure',
1129 'err_msg' => 'improper digest cache structure',
1130 'function' => \&digest_cache_structure,
1135 'category' => 'Rijndael SPA',
1136 'subcategory' => 'client+server',
1137 'detail' => 'non-base64 altered SPA data',
1138 'err_msg' => 'allowed improper SPA data',
1139 'function' => \&altered_non_base64_spa_data,
1140 'cmdline' => $default_client_args,
1141 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1142 "$fwknopdCmd $default_server_conf_args $intf_str",
1146 'category' => 'Rijndael SPA',
1147 'subcategory' => 'client+server',
1148 'detail' => 'base64 altered SPA data',
1149 'err_msg' => 'allowed improper SPA data',
1150 'function' => \&altered_base64_spa_data,
1151 'cmdline' => $default_client_args,
1152 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1153 "$fwknopdCmd $default_server_conf_args $intf_str",
1157 'category' => 'Rijndael SPA',
1158 'subcategory' => 'client+server',
1159 'detail' => 'appended data to SPA pkt',
1160 'err_msg' => 'allowed improper SPA data',
1161 'function' => \&appended_spa_data,
1162 'cmdline' => $default_client_args,
1163 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1164 "$fwknopdCmd $default_server_conf_args $intf_str",
1168 'category' => 'Rijndael SPA',
1169 'subcategory' => 'client+server',
1170 'detail' => 'prepended data to SPA pkt',
1171 'err_msg' => 'allowed improper SPA data',
1172 'function' => \&prepended_spa_data,
1173 'cmdline' => $default_client_args,
1174 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1175 "$fwknopdCmd $default_server_conf_args $intf_str",
1180 'category' => 'GnuPG (GPG) SPA',
1181 'subcategory' => 'client+server',
1182 'detail' => 'complete cycle (tcp/22 ssh)',
1183 'err_msg' => 'could not complete SPA cycle',
1184 'function' => \&spa_cycle,
1185 'cmdline' => $default_client_gpg_args,
1186 'fwknopd_cmdline' => $default_server_gpg_args,
1187 'fw_rule_created' => $NEW_RULE_REQUIRED,
1188 'fw_rule_removed' => $NEW_RULE_REMOVED,
1192 'category' => 'GnuPG (GPG) SPA',
1193 'subcategory' => 'client+server',
1194 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1195 'err_msg' => 'could not complete SPA cycle',
1196 'function' => \&spa_cycle,
1197 'cmdline' => $default_client_gpg_args,
1198 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1199 "$valgrind_str $fwknopdCmd -c $default_conf " .
1200 "-a $multi_gpg_access_conf $intf_str " .
1201 "-d $default_digest_file -p $default_pid_file",
1202 'fw_rule_created' => $NEW_RULE_REQUIRED,
1203 'fw_rule_removed' => $NEW_RULE_REMOVED,
1208 'category' => 'GnuPG (GPG) SPA',
1209 'subcategory' => 'client+server',
1210 'detail' => 'complete cycle (tcp/23 telnet)',
1211 'err_msg' => 'could not complete SPA cycle',
1212 'function' => \&spa_cycle,
1213 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1214 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1215 "$local_key_file --verbose --verbose " .
1216 "--gpg-recipient-key $gpg_server_key " .
1217 "--gpg-signer-key $gpg_client_key " .
1218 "--gpg-home-dir $gpg_client_home_dir",
1219 'fwknopd_cmdline' => $default_server_gpg_args,
1220 'fw_rule_created' => $NEW_RULE_REQUIRED,
1221 'fw_rule_removed' => $NEW_RULE_REMOVED,
1225 'category' => 'GnuPG (GPG) SPA',
1226 'subcategory' => 'client+server',
1227 'detail' => 'complete cycle (tcp/9418 git)',
1228 'err_msg' => 'could not complete SPA cycle',
1229 'function' => \&spa_cycle,
1230 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1231 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1232 "$local_key_file --verbose --verbose " .
1233 "--gpg-recipient-key $gpg_server_key " .
1234 "--gpg-signer-key $gpg_client_key " .
1235 "--gpg-home-dir $gpg_client_home_dir",
1236 'fwknopd_cmdline' => $default_server_gpg_args,
1237 'fw_rule_created' => $NEW_RULE_REQUIRED,
1238 'fw_rule_removed' => $NEW_RULE_REMOVED,
1242 'category' => 'GnuPG (GPG) SPA',
1243 'subcategory' => 'client+server',
1244 'detail' => 'complete cycle (udp/53 dns)',
1245 'err_msg' => 'could not complete SPA cycle',
1246 'function' => \&spa_cycle,
1247 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1248 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1249 "$local_key_file --verbose --verbose " .
1250 "--gpg-recipient-key $gpg_server_key " .
1251 "--gpg-signer-key $gpg_client_key " .
1252 "--gpg-home-dir $gpg_client_home_dir",
1253 'fwknopd_cmdline' => $default_server_gpg_args,
1254 'fw_rule_created' => $NEW_RULE_REQUIRED,
1255 'fw_rule_removed' => $NEW_RULE_REMOVED,
1260 'category' => 'GnuPG (GPG) SPA',
1261 'subcategory' => 'client+server',
1262 'detail' => 'replay attack detection',
1263 'err_msg' => 'could not detect replay attack',
1264 'function' => \&replay_detection,
1265 'cmdline' => $default_client_gpg_args,
1266 'fwknopd_cmdline' => $default_server_gpg_args,
1271 'category' => 'GnuPG (GPG) SPA',
1272 'subcategory' => 'client+server',
1273 'detail' => 'non-base64 altered SPA data',
1274 'err_msg' => 'allowed improper SPA data',
1275 'function' => \&altered_non_base64_spa_data,
1276 'cmdline' => $default_client_gpg_args,
1277 'fwknopd_cmdline' => $default_server_gpg_args,
1281 'category' => 'GnuPG (GPG) SPA',
1282 'subcategory' => 'client+server',
1283 'detail' => 'base64 altered SPA data',
1284 'err_msg' => 'allowed improper SPA data',
1285 'function' => \&altered_base64_spa_data,
1286 'cmdline' => $default_client_gpg_args,
1287 'fwknopd_cmdline' => $default_server_gpg_args,
1291 'category' => 'GnuPG (GPG) SPA',
1292 'subcategory' => 'client+server',
1293 'detail' => 'appended data to SPA pkt',
1294 'err_msg' => 'allowed improper SPA data',
1295 'function' => \&appended_spa_data,
1296 'cmdline' => $default_client_gpg_args,
1297 'fwknopd_cmdline' => $default_server_gpg_args,
1301 'category' => 'GnuPG (GPG) SPA',
1302 'subcategory' => 'client+server',
1303 'detail' => 'prepended data to SPA pkt',
1304 'err_msg' => 'allowed improper SPA data',
1305 'function' => \&prepended_spa_data,
1306 'cmdline' => $default_client_gpg_args,
1307 'fwknopd_cmdline' => $default_server_gpg_args,
1311 'category' => 'GnuPG (GPG) SPA',
1312 'subcategory' => 'client+server',
1313 'detail' => 'spoof username (tcp/22 ssh)',
1314 'err_msg' => 'could not spoof username',
1315 'function' => \&spoof_username,
1316 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1317 'fwknopd_cmdline' => $default_server_gpg_args,
1322 'category' => 'GnuPG (GPG) SPA',
1323 'subcategory' => 'server',
1324 'detail' => 'digest cache structure',
1325 'err_msg' => 'improper digest cache structure',
1326 'function' => \&digest_cache_structure,
1331 'category' => 'profile coverage',
1332 'detail' => 'gcov profile coverage',
1333 'err_msg' => 'profile coverage failed',
1334 'function' => \&profile_coverage,
1340 'category' => $REQUIRED,
1341 'subcategory' => $OPTIONAL,
1342 'detail' => $REQUIRED,
1343 'function' => $REQUIRED,
1344 'binary' => $OPTIONAL,
1345 'cmdline' => $OPTIONAL,
1346 'fwknopd_cmdline' => $OPTIONAL,
1347 'fatal' => $OPTIONAL,
1348 'exec_err' => $OPTIONAL,
1349 'fw_rule_created' => $OPTIONAL,
1350 'fw_rule_removed' => $OPTIONAL,
1351 'server_conf' => $OPTIONAL,
1352 'positive_output_matches' => $OPTIONAL,
1353 'negative_output_matches' => $OPTIONAL,
1354 'server_positive_output_matches' => $OPTIONAL,
1355 'server_negative_output_matches' => $OPTIONAL,
1359 &diff_test_results();
1363 ### make sure everything looks as expected before continuing
1366 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1367 " args: @args_cp\n\n"
1370 ### save the results from any previous test suite run
1371 ### so that we can potentially compare them with --diff
1372 if ($saved_last_results) {
1373 &logr(" Saved results from previous run " .
1374 "to: ${output_dir}.last/\n\n");
1377 ### main loop through all of the tests
1378 for my $test_hr (@tests) {
1379 &run_test($test_hr);
1382 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1384 copy $logfile, "$output_dir/$logfile" or die $!;
1388 #===================== end main =======================
1391 my $test_hr = shift;
1393 my $msg = "[$test_hr->{'category'}]";
1394 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1395 $msg .= " $test_hr->{'detail'}";
1397 return unless &process_include_exclude($msg);
1407 $current_test_file = "$output_dir/$executed.test";
1408 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1410 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1411 $test_hr->{'msg'} = $msg;
1412 if (&{$test_hr->{'function'}}($test_hr)) {
1413 &logr("pass ($executed)\n");
1416 &logr("fail ($executed)\n");
1419 if ($test_hr->{'fatal'} eq $YES) {
1420 die "[*] required test failed, exiting.";
1427 sub process_include_exclude() {
1430 ### inclusions/exclusions
1431 if (@tests_to_include) {
1433 for my $test (@tests_to_include) {
1434 if ($msg =~ /$test/) {
1439 return 0 unless $found;
1441 if (@tests_to_exclude) {
1443 for my $test (@tests_to_exclude) {
1444 if ($msg =~ /$test/) {
1454 sub diff_test_results() {
1455 die "[*] Need results from a previous run before running --diff"
1456 unless -d "${output_dir}.last";
1457 die "[*] Current results set does not exist." unless -d $output_dir;
1459 my %current_tests = ();
1460 my %previous_tests = ();
1462 ### Only diff results for matching tests (parse the logfile to see which
1463 ### test numbers match across the two test cycles).
1464 &build_results_hash(\%current_tests, $output_dir);
1465 &build_results_hash(\%previous_tests, "${output_dir}.last");
1467 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1468 keys %current_tests) {
1469 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1470 my $current_num = $current_tests{$test_msg}{'num'};
1471 if (defined $previous_tests{$test_msg}) {
1472 print "[+] Checking: $test_msg\n";
1473 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1474 my $previous_num = $previous_tests{$test_msg}{'num'};
1475 if ($current_result ne $previous_result) {
1476 print " DIFF: **$current_result** $test_msg\n";
1479 &diff_results($previous_num, $current_num);
1487 sub diff_results() {
1488 my ($previous_num, $current_num) = @_;
1490 ### edit out any valgrind "==354==" prefixes
1491 my $valgrind_search_re = qr/^==\d+==\s/;
1493 ### remove CMD timestamps
1494 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1496 for my $file ("${output_dir}.last/${previous_num}.test",
1497 "${output_dir}.last/${previous_num}_fwknopd.test",
1498 "${output_dir}/${current_num}.test",
1499 "${output_dir}/${current_num}_fwknopd.test",
1501 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1502 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1505 if (-e "${output_dir}.last/${previous_num}.test"
1506 and -e "${output_dir}/${current_num}.test") {
1507 system "diff -u ${output_dir}.last/${previous_num}.test " .
1508 "${output_dir}/${current_num}.test";
1511 if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1512 and -e "${output_dir}/${current_num}_fwknopd.test") {
1513 system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1514 "${output_dir}/${current_num}_fwknopd.test";
1520 sub build_results_hash() {
1521 my ($hr, $dir) = @_;
1523 open F, "< $dir/$logfile" or die $!;
1525 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1526 $hr->{$1}{'pass_fail'} = $2;
1527 $hr->{$1}{'num'} = $3;
1533 sub compile_warnings() {
1535 ### 'make clean' as root
1536 return 0 unless &run_cmd('make -C .. clean',
1537 $cmd_out_tmp, $current_test_file);
1540 my $username = getpwuid((stat($configure_path))[4]);
1541 die "[*] Could not determine $configure_path owner"
1544 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1545 $cmd_out_tmp, $current_test_file);
1549 return 0 unless &run_cmd('make -C ..',
1550 $cmd_out_tmp, $current_test_file);
1554 ### look for compilation warnings - something like:
1555 ### warning: ‘test’ is used uninitialized in this function
1556 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
1557 $current_test_file);
1559 ### the new binaries should exist
1560 unless (-e $fwknopCmd and -x $fwknopCmd) {
1561 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1562 $current_test_file);
1564 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1565 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1566 $current_test_file);
1572 sub profile_coverage() {
1574 ### check for any *.gcno files - if they don't exist, then fwknop was
1575 ### not compiled with profile support
1576 unless (glob('../client/*.gcno') and glob('../server/*.gcno')) {
1577 &write_test_file("[-] ../client/*.gcno and " .
1578 "../server/*.gcno files do not exist.\n", $current_test_file);
1582 my $curr_dir = getcwd() or die $!;
1584 ### gcov -b ../client/*.gcno
1585 for my $dir ('../client', '../server', '../lib/.libs') {
1586 next unless -d $dir;
1587 chdir $dir or die $!;
1588 system "$gcov_path -b -u *.gcno > /dev/null 2>&1";
1589 chdir $curr_dir or die $!;
1591 &run_cmd(qq|grep "called 0 returned" $dir/*.gcov|,
1592 $cmd_out_tmp, $current_test_file);
1598 sub binary_exists() {
1599 my $test_hr = shift;
1600 return 0 unless $test_hr->{'binary'};
1602 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1603 ### libfko.so link on OpenBSD)
1605 if ($test_hr->{'binary'} =~ /libfko/) {
1606 unless (-e $test_hr->{'binary'}) {
1607 for my $file (glob("$lib_dir/libfko.so*")) {
1608 if (-e $file and -x $file) {
1609 $test_hr->{'binary'} = $file;
1610 $libfko_bin = $file;
1617 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1621 sub expected_code_version() {
1622 my $test_hr = shift;
1624 unless (-e '../VERSION') {
1625 &write_test_file("[-] ../VERSION file does not exist.\n",
1626 $current_test_file);
1630 open F, '< ../VERSION' or die $!;
1633 if ($line =~ /(\d.*\d)/) {
1635 return 0 unless &run_cmd($test_hr->{'cmdline'},
1636 $cmd_out_tmp, $current_test_file);
1637 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1642 sub client_send_spa_packet() {
1643 my $test_hr = shift;
1645 &write_key('fwknoptest', $local_key_file);
1647 return 0 unless &run_cmd($test_hr->{'cmdline'},
1648 $cmd_out_tmp, $current_test_file);
1649 return 0 unless &file_find_regex([qr/final\spacked/i],
1650 $current_test_file);
1656 my $test_hr = shift;
1658 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1659 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1661 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1662 $rv = 0 unless $fw_rule_created;
1663 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1664 $rv = 0 if $fw_rule_created;
1667 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1668 $rv = 0 unless $fw_rule_removed;
1669 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1670 $rv = 0 if $fw_rule_removed;
1673 if ($test_hr->{'server_positive_output_matches'}) {
1674 $rv = 0 unless &file_find_regex(
1675 $test_hr->{'server_positive_output_matches'},
1679 if ($test_hr->{'server_negative_output_matches'}) {
1680 $rv = 0 if &file_find_regex(
1681 $test_hr->{'server_negative_output_matches'},
1688 sub spoof_username() {
1689 my $test_hr = shift;
1691 my $rv = &spa_cycle($test_hr);
1693 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1694 $current_test_file)) {
1698 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1699 $server_test_file)) {
1706 sub replay_detection() {
1707 my $test_hr = shift;
1709 ### do a complete SPA cycle and then parse the SPA packet out of the
1710 ### current test file and re-send
1712 return 0 unless &spa_cycle($test_hr);
1714 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1717 &write_test_file("[-] could not get SPA packet " .
1718 "from file: $current_test_file\n",
1719 $current_test_file);
1726 'port' => $default_spa_port,
1727 'dst_ip' => $loopback_ip,
1732 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1733 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1735 $rv = 0 unless $server_was_stopped;
1737 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1738 $server_test_file)) {
1745 sub digest_cache_structure() {
1746 my $test_hr = shift;
1749 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1751 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1753 ### the format should be:
1754 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1755 open F, "< $default_digest_file" or
1756 die "[*] could not open $default_digest_file: $!";
1760 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1761 &write_test_file("[-] invalid digest.cache line: $_",
1762 $current_test_file);
1768 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1769 &write_test_file("[+] DBM digest file format, " .
1770 "assuming this is valid.\n", $current_test_file);
1772 ### don't know what kind of file the digest.cache is
1773 &write_test_file("[-] unrecognized file type for " .
1774 "$default_digest_file.\n", $current_test_file);
1779 &write_test_file("[+] valid digest.cache structure.\n",
1780 $current_test_file);
1786 sub server_bpf_ignore_packet() {
1787 my $test_hr = shift;
1790 my $server_was_stopped = 0;
1791 my $fw_rule_created = 0;
1792 my $fw_rule_removed = 0;
1794 unless (&client_send_spa_packet($test_hr)) {
1795 &write_test_file("[-] fwknop client execution error.\n",
1796 $current_test_file);
1800 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1803 &write_test_file("[-] could not get SPA packet " .
1804 "from file: $current_test_file\n", $current_test_file);
1811 'port' => $default_spa_port,
1812 'dst_ip' => $loopback_ip,
1817 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1818 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1820 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1821 $server_test_file)) {
1828 sub altered_non_base64_spa_data() {
1829 my $test_hr = shift;
1832 my $server_was_stopped = 0;
1833 my $fw_rule_created = 0;
1834 my $fw_rule_removed = 0;
1836 unless (&client_send_spa_packet($test_hr)) {
1837 &write_test_file("[-] fwknop client execution error.\n",
1838 $current_test_file);
1842 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1845 &write_test_file("[-] could not get SPA packet " .
1846 "from file: $current_test_file\n", $current_test_file);
1850 ### alter one byte (change to a ":")
1851 $spa_pkt =~ s|^(.{3}).|$1:|;
1856 'port' => $default_spa_port,
1857 'dst_ip' => $loopback_ip,
1862 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1863 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1865 $rv = 0 unless $server_was_stopped;
1870 sub altered_base64_spa_data() {
1871 my $test_hr = shift;
1874 my $server_was_stopped = 0;
1875 my $fw_rule_created = 0;
1876 my $fw_rule_removed = 0;
1878 unless (&client_send_spa_packet($test_hr)) {
1879 &write_test_file("[-] fwknop client execution error.\n",
1880 $current_test_file);
1884 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1887 &write_test_file("[-] could not get SPA packet " .
1888 "from file: $current_test_file\n", $current_test_file);
1892 $spa_pkt =~ s|^(.{3}).|AAAA|;
1897 'port' => $default_spa_port,
1898 'dst_ip' => $loopback_ip,
1903 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1904 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1906 $rv = 0 unless $server_was_stopped;
1908 if ($fw_rule_created) {
1909 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1912 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1915 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1916 $server_test_file)) {
1923 sub appended_spa_data() {
1924 my $test_hr = shift;
1927 my $server_was_stopped = 0;
1928 my $fw_rule_created = 0;
1929 my $fw_rule_removed = 0;
1931 unless (&client_send_spa_packet($test_hr)) {
1932 &write_test_file("[-] fwknop client execution error.\n",
1933 $current_test_file);
1937 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1940 &write_test_file("[-] could not get SPA packet " .
1941 "from file: $current_test_file\n", $current_test_file);
1950 'port' => $default_spa_port,
1951 'dst_ip' => $loopback_ip,
1956 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1957 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1959 $rv = 0 unless $server_was_stopped;
1961 if ($fw_rule_created) {
1962 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1965 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1968 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1969 $server_test_file)) {
1976 sub prepended_spa_data() {
1977 my $test_hr = shift;
1980 my $server_was_stopped = 0;
1981 my $fw_rule_created = 0;
1982 my $fw_rule_removed = 0;
1984 unless (&client_send_spa_packet($test_hr)) {
1985 &write_test_file("[-] fwknop client execution error.\n",
1986 $current_test_file);
1990 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1993 &write_test_file("[-] could not get SPA packet " .
1994 "from file: $current_test_file\n", $current_test_file);
1998 $spa_pkt = 'AAAA' . $spa_pkt;
2003 'port' => $default_spa_port,
2004 'dst_ip' => $loopback_ip,
2009 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2010 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2012 $rv = 0 unless $server_was_stopped;
2014 if ($fw_rule_created) {
2015 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2018 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2021 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2022 $server_test_file)) {
2029 sub server_start() {
2030 my $test_hr = shift;
2032 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2033 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2035 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
2036 $server_test_file)) {
2040 $rv = 0 unless $server_was_stopped;
2046 my $test_hr = shift;
2048 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2049 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2051 $rv = 0 unless $server_was_stopped;
2056 sub server_packet_limit() {
2057 my $test_hr = shift;
2062 'port' => $default_spa_port,
2063 'dst_ip' => $loopback_ip,
2068 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2069 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2071 if (&is_fwknopd_running()) {
2076 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2077 $server_test_file)) {
2081 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2082 $server_test_file)) {
2089 sub server_ignore_small_packets() {
2090 my $test_hr = shift;
2095 'port' => $default_spa_port,
2096 'dst_ip' => $loopback_ip,
2097 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2101 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2102 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2106 if (&is_fwknopd_running()) {
2114 sub client_server_interaction() {
2115 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2118 my $server_was_stopped = 1;
2119 my $fw_rule_created = 1;
2120 my $fw_rule_removed = 0;
2122 ### start fwknopd to monitor for the SPA packet over the loopback interface
2123 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2125 ### give fwknopd a chance to parse its config and start sniffing
2126 ### on the loopback interface
2127 if ($use_valgrind) {
2133 ### send the SPA packet(s) to the server either manually using IO::Socket or
2134 ### with the fwknopd client
2135 if ($spa_client_flag == $USE_CLIENT) {
2136 unless (&client_send_spa_packet($test_hr)) {
2137 &write_test_file("[-] fwknop client execution error.\n",
2138 $current_test_file);
2142 &send_packets($pkts_hr);
2145 ### check to see if the SPA packet resulted in a new fw access rule
2147 while (not &is_fw_rule_active($test_hr)) {
2148 &write_test_file("[-] new fw rule does not exist.\n",
2149 $current_test_file);
2155 $fw_rule_created = 0;
2156 $fw_rule_removed = 0;
2159 &time_for_valgrind() if $use_valgrind;
2161 if ($fw_rule_created) {
2162 sleep 3; ### allow time for rule time out.
2163 if (&is_fw_rule_active($test_hr)) {
2164 &write_test_file("[-] new fw rule not timed out.\n",
2165 $current_test_file);
2168 &write_test_file("[+] new fw rule timed out.\n",
2169 $current_test_file);
2170 $fw_rule_removed = 1;
2174 if (&is_fwknopd_running()) {
2176 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2177 $server_test_file)) {
2178 $server_was_stopped = 0;
2181 &write_test_file("[-] server is not running.\n",
2182 $current_test_file);
2183 $server_was_stopped = 0;
2186 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2189 sub get_spa_packet_from_file() {
2194 my $found_trigger_line = 0;
2195 open F, "< $file" or die "[*] Could not open file $file: $!";
2197 if (/final\spacked/i) {
2198 $found_trigger_line = 1;
2201 next unless $found_trigger_line;
2203 ### the next line with non whitespace is the SPA packet
2214 sub send_packets() {
2215 my $pkts_ar = shift;
2217 open F, ">> $current_test_file" or die $!;
2218 print F "[+] send_packets(): Sending the following packets...\n";
2219 print F Dumper $pkts_ar;
2222 for my $pkt_hr (@$pkts_ar) {
2223 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2224 my $socket = IO::Socket::INET->new(
2225 PeerAddr => $pkt_hr->{'dst_ip'},
2226 PeerPort => $pkt_hr->{'port'},
2227 Proto => $pkt_hr->{'proto'},
2229 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2230 "socket to $pkt_hr->{'dst_ip'}: $!";
2232 $socket->send($pkt_hr->{'data'});
2235 } elsif ($pkt_hr->{'proto'} eq 'http') {
2237 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2241 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2246 sub generic_exec() {
2247 my $test_hr = shift;
2251 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2252 $cmd_out_tmp, $current_test_file);
2254 if ($test_hr->{'exec_err'} eq $YES) {
2255 $rv = 0 if $exec_rv;
2257 $rv = 0 unless $exec_rv;
2260 if ($test_hr->{'positive_output_matches'}) {
2261 $rv = 0 unless &file_find_regex(
2262 $test_hr->{'positive_output_matches'},
2263 $current_test_file);
2266 if ($test_hr->{'negative_output_matches'}) {
2267 $rv = 0 if &file_find_regex(
2268 $test_hr->{'negative_output_matches'},
2269 $current_test_file);
2277 my $test_hr = shift;
2278 return 0 unless $test_hr->{'binary'};
2279 &run_cmd("./hardening-check $test_hr->{'binary'}",
2280 $cmd_out_tmp, $current_test_file);
2281 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2282 $current_test_file);
2286 ### check for stack protection
2287 sub stack_protected_binary() {
2288 my $test_hr = shift;
2289 return 0 unless $test_hr->{'binary'};
2290 &run_cmd("./hardening-check $test_hr->{'binary'}",
2291 $cmd_out_tmp, $current_test_file);
2292 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2293 $current_test_file);
2297 ### check for fortified source functions
2298 sub fortify_source_functions() {
2299 my $test_hr = shift;
2300 return 0 unless $test_hr->{'binary'};
2301 &run_cmd("./hardening-check $test_hr->{'binary'}",
2302 $cmd_out_tmp, $current_test_file);
2303 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2304 $current_test_file);
2308 ### check for read-only relocations
2309 sub read_only_relocations() {
2310 my $test_hr = shift;
2311 return 0 unless $test_hr->{'binary'};
2312 &run_cmd("./hardening-check $test_hr->{'binary'}",
2313 $cmd_out_tmp, $current_test_file);
2314 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2315 $current_test_file);
2319 ### check for immediate binding
2320 sub immediate_binding() {
2321 my $test_hr = shift;
2322 return 0 unless $test_hr->{'binary'};
2323 &run_cmd("./hardening-check $test_hr->{'binary'}",
2324 $cmd_out_tmp, $current_test_file);
2325 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2326 $current_test_file);
2332 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2333 "$default_server_conf_args --fw-list-all",
2334 $cmd_out_tmp, $current_test_file);
2342 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2343 'if [ `which iptables` ]; then iptables -V; fi',
2344 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2345 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2346 'if [ `which gpg` ]; then gpg --version; fi',
2347 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2351 'ls -l /usr/lib/*pcap*',
2352 'ls -l /usr/local/lib/*pcap*',
2353 'ls -l /usr/lib/*fko*',
2354 'ls -l /usr/local/lib/*fko*',
2356 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2358 if ($cmd =~ /^ldd/) {
2359 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2363 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2364 ### to enable gpg tests
2365 unless ($have_gpgme == 3) {
2366 push @tests_to_exclude, "GPG";
2372 sub time_for_valgrind() {
2374 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2375 "grep valgrind |grep -v perl | grep -v grep",
2376 $cmd_out_tmp, $current_test_file)) {
2384 sub anonymize_results() {
2386 die "[*] $output_dir does not exist" unless -d $output_dir;
2387 die "[*] $logfile does not exist, has $0 been executed?"
2390 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2393 ### remove non-loopback IP addresses
2394 my $search_re = qr/\b127\.0\.0\.1\b/;
2395 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2396 $search_re = qr/\b127\.0\.0\.2\b/;
2397 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2398 $search_re = qr/\b0\.0\.0\.0\b/;
2399 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2400 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2401 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2402 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2403 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2404 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2406 ### remove hostname from any uname output
2407 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2408 system "perl -p -i -e 'undef \$/; s|$search_re" .
2409 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2411 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2412 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2415 system "tar cvfz $tarfile $logfile $output_dir";
2416 print "[+] Anonymized test results file: $tarfile\n";
2425 my $test_hr = shift;
2427 open F, "> $default_pid_file" or die $!;
2431 &server_start($test_hr);
2433 open F, "< $default_pid_file" or die $!;
2445 sub start_fwknopd() {
2446 my $test_hr = shift;
2448 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2451 die "[*] Could not fork: $!" unless defined $pid;
2455 ### we are the child, so start fwknopd
2456 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2457 $server_cmd_tmp, $server_test_file);
2463 my ($key, $file) = @_;
2465 open K, "> $file" or die "[*] Could not open $file: $!";
2466 print K "$loopback_ip: $key\n";
2467 print K "localhost: $key\n";
2468 print K "some.host.through.proxy.com: $key\n";
2474 open C, ">> $current_test_file"
2475 or die "[*] Could not open $current_test_file: $!";
2476 print C "\n" . localtime() . " [+] PID dump:\n";
2478 &run_cmd("ps auxww | grep knop |grep -v grep",
2479 $cmd_out_tmp, $current_test_file);
2484 my ($cmd, $cmd_out, $file) = @_;
2488 or die "[*] Could not open $file: $!";
2489 print F localtime() . " CMD: $cmd\n";
2493 or die "[*] Could not open $file: $!";
2494 print F localtime() . " CMD: $cmd\n";
2498 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2500 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2501 my @cmd_lines = <C>;
2504 open F, ">> $file" or die "[*] Could not open $file: $!";
2505 print F $_ for @cmd_lines;
2518 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2527 $|++; ### turn off buffering
2529 $< == 0 && $> == 0 or
2530 die "[*] $0: You must be root (or equivalent ",
2531 "UID 0 account) to effectively test fwknop";
2533 ### validate test hashes
2535 for my $test_hr (@tests) {
2536 for my $key (keys %test_keys) {
2537 if ($test_keys{$key} == $REQUIRED) {
2538 die "[*] Missing '$key' element in hash: $hash_num"
2539 unless defined $test_hr->{$key};
2541 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2547 if ($use_valgrind) {
2548 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2549 unless -e $valgrindCmd and -x $valgrindCmd;
2552 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2553 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2555 for my $file ($configure_path,
2558 $default_access_conf,
2559 $no_source_match_access_conf,
2560 $ip_source_match_access_conf,
2561 $subnet_source_match_access_conf,
2562 $no_subnet_source_match_access_conf,
2563 $no_multi_source_match_access_conf,
2564 $multi_source_match_access_conf,
2565 $open_ports_access_conf,
2566 $mismatch_open_ports_access_conf,
2567 $require_user_access_conf,
2568 $mismatch_user_access_conf,
2569 $require_src_access_conf,
2570 $multi_gpg_access_conf,
2571 $multi_stanzas_access_conf,
2572 $expired_access_conf,
2573 $expired_epoch_access_conf,
2574 $future_expired_access_conf,
2575 $invalid_expire_access_conf,
2576 $force_nat_access_conf,
2578 die "[*] $file does not exist" unless -e $file;
2581 if (-d $output_dir) {
2582 if (-d "${output_dir}.last") {
2583 rmtree "${output_dir}.last"
2584 or die "[*] rmtree ${output_dir}.last $!";
2586 mkdir "${output_dir}.last"
2587 or die "[*] ${output_dir}.last: $!";
2588 for my $file (glob("$output_dir/*.test")) {
2589 if ($file =~ m|.*/(.*)|) {
2590 copy $file, "${output_dir}.last/$1" or die $!;
2593 if (-e "$output_dir/init") {
2594 copy "$output_dir/init", "${output_dir}.last/init";
2597 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2599 $saved_last_results = 1;
2601 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2603 unless (-d $run_dir) {
2604 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2607 for my $file (glob("$output_dir/*.test")) {
2608 unlink $file or die "[*] Could not unlink($file)";
2610 if (-e "$output_dir/init") {
2611 unlink "$output_dir/init" or die $!;
2615 unlink $logfile or die $!;
2618 if ($test_include) {
2619 @tests_to_include = split /\s*,\s*/, $test_include;
2621 if ($test_exclude) {
2622 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2625 ### make sure no fwknopd instance is currently running
2626 die "[*] Please stop the running fwknopd instance."
2627 if &is_fwknopd_running();
2629 unless ($enable_recompilation_warnings_check) {
2630 push @tests_to_exclude, 'recompilation';
2633 unless ($enable_profile_coverage_check) {
2634 push @tests_to_exclude, 'profile coverage';
2637 $sudo_path = &find_command('sudo');
2639 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2640 ### disable compilation checks
2641 push @tests_to_exclude, 'recompilation';
2644 $gcov_path = &find_command('gcov');
2647 if ($enable_profile_coverage_check) {
2648 for my $extension ('*.gcov', '*.gcda') {
2649 ### remove profile output from any previous run
2650 system qq{find .. -name $extension | xargs rm 2> /dev/null};
2654 push @tests_to_exclude, 'profile coverage';
2657 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2660 $platform = 'linux';
2666 unless ($platform eq 'linux') {
2667 push @tests_to_exclude, 'NAT';
2673 sub identify_loopback_intf() {
2674 return if $loopback_intf;
2678 ### lo Link encap:Local Loopback
2679 ### inet addr:127.0.0.1 Mask:255.0.0.0
2680 ### inet6 addr: ::1/128 Scope:Host
2681 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2682 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2683 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2684 ### collisions:0 txqueuelen:0
2685 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2689 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2690 ### options=3<RXCSUM,TXCSUM>
2691 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2692 ### inet6 ::1 prefixlen 128
2693 ### inet 127.0.0.1 netmask 0xff000000
2694 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2697 my $found_loopback_intf = 0;
2699 my $cmd = 'ifconfig -a';
2700 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2702 if (/^(\S+?):?\s+.*loopback/i) {
2706 if (/^\S/ and $intf and not $found_loopback_intf) {
2707 ### should not happen
2710 if ($intf and /\b127\.0\.0\.1\b/) {
2711 $found_loopback_intf = 1;
2717 die "[*] could not determine loopback interface, use --loopback <name>"
2718 unless $found_loopback_intf;
2720 $loopback_intf = $intf;
2725 sub is_fw_rule_active() {
2726 my $test_hr = shift;
2728 my $conf_args = $default_server_conf_args;
2730 if ($test_hr->{'server_conf'}) {
2731 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2732 "-d $default_digest_file -p $default_pid_file";
2735 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2736 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2737 $cmd_out_tmp, $current_test_file);
2741 sub is_fwknopd_running() {
2743 sleep 2 if $use_valgrind;
2745 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2746 "--status", $cmd_out_tmp, $current_test_file);
2748 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2753 sub stop_fwknopd() {
2755 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2756 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2758 if ($use_valgrind) {
2759 &time_for_valgrind();
2767 sub file_find_regex() {
2768 my ($re_ar, $file) = @_;
2771 my @write_lines = ();
2773 open F, "< $file" or die "[*] Could not open $file: $!";
2776 next LINE if $line =~ /file_file_regex\(\)/;
2777 for my $re (@$re_ar) {
2779 push @write_lines, "[.] file_find_regex() " .
2780 "Matched '$re' with line: $line";
2789 for my $line (@write_lines) {
2790 &write_test_file($line, $file);
2793 &write_test_file("[.] find_find_regex() Did not " .
2794 "match any regex in: '@$re_ar'\n", $file);
2800 sub find_command() {
2804 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2806 if (m|^(/.*$cmd)$|) {
2815 sub write_test_file() {
2816 my ($msg, $file) = @_;
2820 or die "[*] Could not open $file: $!";
2825 or die "[*] Could not open $file: $!";
2835 open F, ">> $logfile" or die $!;