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 $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
35 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
36 my $default_digest_file = "$run_dir/digest.cache";
37 my $default_pid_file = "$run_dir/fwknopd.pid";
38 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
39 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
40 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
41 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
42 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
43 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
44 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
45 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
46 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
47 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
48 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
49 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
50 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
51 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
53 my $fwknopCmd = '../client/.libs/fwknop';
54 my $fwknopdCmd = '../server/.libs/fwknopd';
55 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
56 my $valgrindCmd = '/usr/bin/valgrind';
58 my $gpg_server_key = '361BBAD4';
59 my $gpg_client_key = '6A3FAD56';
61 my $loopback_ip = '127.0.0.1';
62 my $fake_ip = '127.0.0.2';
63 my $internal_nat_host = '192.168.1.2';
64 my $force_nat_host = '192.168.1.123';
65 my $default_spa_port = 62201;
66 my $non_std_spa_port = 12345;
68 my $spoof_user = 'testuser';
69 #================== end config ===================
74 my $test_include = '';
75 my @tests_to_include = ();
76 my $test_exclude = '';
77 my @tests_to_exclude = ();
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;
89 my $enable_profile_coverage_check = 0;
97 my $USE_PREDEF_PKTS = 1;
101 my $NEW_RULE_REQUIRED = 1;
102 my $REQUIRE_NO_NEW_RULE = 2;
103 my $NEW_RULE_REMOVED = 1;
104 my $REQUIRE_NO_NEW_REMOVED = 2;
106 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
110 exit 1 unless GetOptions(
111 'Anonymize-results' => \$anonymize_results,
112 'fwknop-path=s' => \$fwknopCmd,
113 'fwknopd-path=s' => \$fwknopdCmd,
114 'libfko-path=s' => \$libfko_bin,
115 'loopback-intf=s' => \$loopback_intf,
116 'test-include=s' => \$test_include,
117 'include=s' => \$test_include, ### synonym
118 'test-exclude=s' => \$test_exclude,
119 'exclude=s' => \$test_exclude, ### synonym
120 'enable-recompile-check' => \$enable_recompilation_warnings_check,
121 'enable-profile-coverage-check' => \$enable_profile_coverage_check,
122 'List-mode' => \$list_mode,
123 'enable-valgrind' => \$use_valgrind,
124 'valgrind-path=s' => \$valgrindCmd,
125 'diff' => \$diff_mode,
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' => 'packet aging (past) (tcp/22 ssh)',
605 'err_msg' => 'old SPA packet accepted',
606 'function' => \&spa_cycle,
607 'cmdline' => "$default_client_args --time-offset-minus 300s",
608 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
609 "$fwknopdCmd $default_server_conf_args $intf_str",
610 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
611 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
615 'category' => 'Rijndael SPA',
616 'subcategory' => 'client+server',
617 'detail' => 'packet aging (future) (tcp/22 ssh)',
618 'err_msg' => 'future SPA packet accepted',
619 'function' => \&spa_cycle,
620 'cmdline' => "$default_client_args --time-offset-plus 300s",
621 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
622 "$fwknopdCmd $default_server_conf_args $intf_str",
623 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
624 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
628 'category' => 'Rijndael SPA',
629 'subcategory' => 'client+server',
630 'detail' => 'expired stanza (tcp/22 ssh)',
631 'err_msg' => 'SPA packet accepted',
632 'function' => \&spa_cycle,
633 'cmdline' => $default_client_args,
634 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
635 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
636 "-d $default_digest_file -p $default_pid_file $intf_str",
637 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
638 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
642 'category' => 'Rijndael SPA',
643 'subcategory' => 'client+server',
644 'detail' => 'invalid expire date (tcp/22 ssh)',
645 'err_msg' => 'SPA packet accepted',
646 'function' => \&spa_cycle,
647 'cmdline' => $default_client_args,
648 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
649 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
650 "-d $default_digest_file -p $default_pid_file $intf_str",
651 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
652 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
656 'category' => 'Rijndael SPA',
657 'subcategory' => 'client+server',
658 'detail' => 'expired epoch stanza (tcp/22 ssh)',
659 'err_msg' => 'SPA packet accepted',
660 'function' => \&spa_cycle,
661 'cmdline' => $default_client_args,
662 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
663 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
664 "-d $default_digest_file -p $default_pid_file $intf_str",
665 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
666 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
670 'category' => 'Rijndael SPA',
671 'subcategory' => 'client+server',
672 'detail' => 'future expired stanza (tcp/22 ssh)',
673 'err_msg' => 'SPA packet not accepted',
674 'function' => \&spa_cycle,
675 'cmdline' => $default_client_args,
676 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
677 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
678 "-d $default_digest_file -p $default_pid_file $intf_str",
679 'fw_rule_created' => $NEW_RULE_REQUIRED,
680 'fw_rule_removed' => $NEW_RULE_REMOVED,
685 'category' => 'Rijndael SPA',
686 'subcategory' => 'client+server',
687 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
688 'err_msg' => "improper OPEN_PORTS result",
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 $open_ports_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,
699 'category' => 'Rijndael SPA',
700 'subcategory' => 'client+server',
701 'detail' => 'OPEN_PORTS mismatch',
702 'err_msg' => "SPA packet accepted",
703 'function' => \&spa_cycle,
704 'cmdline' => $default_client_args,
705 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
706 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
707 "-d $default_digest_file -p $default_pid_file $intf_str",
708 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
709 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
713 'category' => 'Rijndael SPA',
714 'subcategory' => 'client+server',
715 'detail' => 'require user (tcp/22 ssh)',
716 'err_msg' => "missed require user criteria",
717 'function' => \&spa_cycle,
718 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
719 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
720 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
721 "-d $default_digest_file -p $default_pid_file $intf_str",
722 'fw_rule_created' => $NEW_RULE_REQUIRED,
723 'fw_rule_removed' => $NEW_RULE_REMOVED,
727 'category' => 'Rijndael SPA',
728 'subcategory' => 'client+server',
729 'detail' => 'user mismatch (tcp/22 ssh)',
730 'err_msg' => "improper user accepted for access",
731 'function' => \&user_mismatch,
732 'function' => \&spa_cycle,
733 'cmdline' => $default_client_args,
734 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
735 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
736 "-d $default_digest_file -p $default_pid_file $intf_str",
737 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
738 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
742 'category' => 'Rijndael SPA',
743 'subcategory' => 'client+server',
744 'detail' => 'require src (tcp/22 ssh)',
745 'err_msg' => "fw rule not created",
746 'function' => \&spa_cycle,
747 'cmdline' => $default_client_args,
748 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
749 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
750 "-d $default_digest_file -p $default_pid_file $intf_str",
751 'fw_rule_created' => $NEW_RULE_REQUIRED,
752 'fw_rule_removed' => $NEW_RULE_REMOVED,
756 'category' => 'Rijndael SPA',
757 'subcategory' => 'client+server',
758 'detail' => 'mismatch require src (tcp/22 ssh)',
759 'err_msg' => "fw rule created",
760 'function' => \&spa_cycle,
761 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
762 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
763 "$local_key_file --verbose --verbose",
764 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
765 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
766 "-d $default_digest_file -p $default_pid_file $intf_str",
767 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
768 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
773 'category' => 'Rijndael SPA',
774 'subcategory' => 'client+server',
775 'detail' => 'IP filtering (tcp/22 ssh)',
776 'err_msg' => "did not filter $loopback_ip",
777 'function' => \&spa_cycle,
778 'cmdline' => $default_client_args,
779 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
780 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
781 "-d $default_digest_file -p $default_pid_file $intf_str",
782 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
783 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
787 'category' => 'Rijndael SPA',
788 'subcategory' => 'client+server',
789 'detail' => 'subnet filtering (tcp/22 ssh)',
790 'err_msg' => "did not filter $loopback_ip",
791 'function' => \&spa_cycle,
792 'cmdline' => $default_client_args,
793 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
794 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
795 "-d $default_digest_file -p $default_pid_file $intf_str",
796 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
797 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
801 'category' => 'Rijndael SPA',
802 'subcategory' => 'client+server',
803 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
804 'err_msg' => "did not filter $loopback_ip",
805 'function' => \&spa_cycle,
806 'cmdline' => $default_client_args,
807 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
808 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
809 "-d $default_digest_file -p $default_pid_file $intf_str",
810 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
811 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
815 'category' => 'Rijndael SPA',
816 'subcategory' => 'client+server',
817 'detail' => 'IP match (tcp/22 ssh)',
818 'err_msg' => "did not filter $loopback_ip",
819 'function' => \&spa_cycle,
820 'cmdline' => $default_client_args,
821 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
822 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
823 "-d $default_digest_file -p $default_pid_file $intf_str",
824 'fw_rule_created' => $NEW_RULE_REQUIRED,
825 'fw_rule_removed' => $NEW_RULE_REMOVED,
829 'category' => 'Rijndael SPA',
830 'subcategory' => 'client+server',
831 'detail' => 'subnet match (tcp/22 ssh)',
832 'err_msg' => "did not filter $loopback_ip",
833 'function' => \&spa_cycle,
834 'cmdline' => $default_client_args,
835 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
836 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
837 "-d $default_digest_file -p $default_pid_file $intf_str",
838 'fw_rule_created' => $NEW_RULE_REQUIRED,
839 'fw_rule_removed' => $NEW_RULE_REMOVED,
843 'category' => 'Rijndael SPA',
844 'subcategory' => 'client+server',
845 'detail' => 'multi IP/net match (tcp/22 ssh)',
846 'err_msg' => "did not filter $loopback_ip",
847 'function' => \&spa_cycle,
848 'cmdline' => $default_client_args,
849 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
850 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
851 "-d $default_digest_file -p $default_pid_file $intf_str",
852 'fw_rule_created' => $NEW_RULE_REQUIRED,
853 'fw_rule_removed' => $NEW_RULE_REMOVED,
857 'category' => 'Rijndael SPA',
858 'subcategory' => 'client+server',
859 'detail' => 'multi access stanzas (tcp/22 ssh)',
860 'err_msg' => "could not complete SPA cycle",
861 'function' => \&spa_cycle,
862 'cmdline' => $default_client_args,
863 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
864 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
865 "-d $default_digest_file -p $default_pid_file $intf_str",
866 'fw_rule_created' => $NEW_RULE_REQUIRED,
867 'fw_rule_removed' => $NEW_RULE_REMOVED,
871 'category' => 'Rijndael SPA',
872 'subcategory' => 'client+server',
873 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
874 'err_msg' => "could not complete SPA cycle",
875 'function' => \&spa_cycle,
876 'cmdline' => $default_client_args,
877 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
878 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
879 "-d $default_digest_file -p $default_pid_file $intf_str",
880 'fw_rule_created' => $NEW_RULE_REQUIRED,
881 'fw_rule_removed' => $NEW_RULE_REMOVED,
886 'category' => 'Rijndael SPA',
887 'subcategory' => 'client+server',
888 'detail' => "non-enabled NAT (tcp/22 ssh)",
889 'err_msg' => "SPA packet not filtered",
890 'function' => \&spa_cycle,
891 'cmdline' => "$default_client_args -N $internal_nat_host:22",
892 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
893 "$fwknopdCmd $default_server_conf_args $intf_str",
894 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
895 'server_conf' => $nat_conf,
896 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
900 'category' => 'Rijndael SPA',
901 'subcategory' => 'client+server',
902 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
903 'err_msg' => "could not complete NAT SPA cycle",
904 'function' => \&spa_cycle,
905 'cmdline' => "$default_client_args -N $internal_nat_host:22",
906 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
907 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
908 "-d $default_digest_file -p $default_pid_file $intf_str",
909 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
910 'fw_rule_created' => $NEW_RULE_REQUIRED,
911 'fw_rule_removed' => $NEW_RULE_REMOVED,
912 'server_conf' => $nat_conf,
917 'category' => 'Rijndael SPA',
918 'subcategory' => 'client+server',
919 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
920 'err_msg' => "could not complete NAT SPA cycle",
921 'function' => \&spa_cycle,
922 'cmdline' => $default_client_args,
923 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
924 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
925 "-d $default_digest_file -p $default_pid_file $intf_str",
926 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
927 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
928 'fw_rule_created' => $NEW_RULE_REQUIRED,
929 'fw_rule_removed' => $NEW_RULE_REMOVED,
930 'server_conf' => $nat_conf,
934 'category' => 'Rijndael SPA',
935 'subcategory' => 'client+server',
936 'detail' => 'ECB mode (tcp/22 ssh)',
937 'err_msg' => 'could not complete SPA cycle',
938 'function' => \&spa_cycle,
939 'cmdline' => "$default_client_args -M ecb",
940 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
941 "$fwknopdCmd -c $default_conf -a $ecb_mode_access_conf " .
942 "-d $default_digest_file -p $default_pid_file $intf_str",
943 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
944 'fw_rule_created' => $NEW_RULE_REQUIRED,
945 'fw_rule_removed' => $NEW_RULE_REMOVED,
949 'category' => 'Rijndael SPA',
950 'subcategory' => 'client+server',
951 'detail' => 'CFB mode (tcp/22 ssh)',
952 'err_msg' => 'could not complete SPA cycle',
953 'function' => \&spa_cycle,
954 'cmdline' => "$default_client_args -M cfb",
955 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
956 "$fwknopdCmd -c $default_conf -a $cfb_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' => 'CTR mode (tcp/22 ssh)',
967 'err_msg' => 'could not complete SPA cycle',
968 'function' => \&spa_cycle,
969 'cmdline' => "$default_client_args -M ctr",
970 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
971 "$fwknopdCmd -c $default_conf -a $ctr_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' => 'OFB mode (tcp/22 ssh)',
982 'err_msg' => 'could not complete SPA cycle',
983 'function' => \&spa_cycle,
984 'cmdline' => "$default_client_args -M ofb",
985 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
986 "$fwknopdCmd -c $default_conf -a $ofb_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,
995 'category' => 'Rijndael SPA',
996 'subcategory' => 'client+server',
997 'detail' => 'mode mismatch (tcp/22 ssh)',
998 'err_msg' => 'server accepted mismatch enc mode',
999 'function' => \&spa_cycle,
1000 'cmdline' => "$default_client_args -M ecb",
1001 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1002 "$fwknopdCmd -c $default_conf -a $default_access_conf " .
1003 "-d $default_digest_file -p $default_pid_file $intf_str",
1004 'server_positive_output_matches' => [qr/Decryption\sfailed/i],
1005 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
1010 'category' => 'Rijndael SPA',
1011 'subcategory' => 'client+server',
1012 'detail' => 'complete cycle (tcp/23 telnet)',
1013 'err_msg' => 'could not complete SPA cycle',
1014 'function' => \&spa_cycle,
1015 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1016 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1017 "$local_key_file --verbose --verbose",
1018 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1019 "$fwknopdCmd $default_server_conf_args $intf_str",
1020 'fw_rule_created' => $NEW_RULE_REQUIRED,
1021 'fw_rule_removed' => $NEW_RULE_REMOVED,
1025 'category' => 'Rijndael SPA',
1026 'subcategory' => 'client+server',
1027 'detail' => 'complete cycle (tcp/9418 git)',
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/9418 -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 (udp/53 dns)',
1043 'err_msg' => 'could not complete SPA cycle',
1044 'function' => \&spa_cycle,
1045 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1046 "$fwknopCmd -A udp/53 -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' => "-P bpf SPA over port $non_std_spa_port",
1058 'err_msg' => 'could not complete SPA cycle',
1059 'function' => \&spa_cycle,
1060 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1061 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1062 "$fwknopdCmd $default_server_conf_args $intf_str " .
1063 qq|-P "udp port $non_std_spa_port"|,
1064 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1065 'fw_rule_created' => $NEW_RULE_REQUIRED,
1066 'fw_rule_removed' => $NEW_RULE_REMOVED,
1071 'category' => 'Rijndael SPA',
1072 'subcategory' => 'client+server',
1073 'detail' => 'random SPA port (tcp/22 ssh)',
1074 'err_msg' => 'could not complete SPA cycle',
1075 'function' => \&spa_cycle,
1076 'cmdline' => "$default_client_args -r",
1077 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1078 "$fwknopdCmd $default_server_conf_args $intf_str " .
1080 'fw_rule_created' => $NEW_RULE_REQUIRED,
1081 'fw_rule_removed' => $NEW_RULE_REMOVED,
1086 'category' => 'Rijndael SPA',
1087 'subcategory' => 'client+server',
1088 'detail' => 'spoof username (tcp/22)',
1089 'err_msg' => 'could not spoof username',
1090 'function' => \&spoof_username,
1091 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1092 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1093 "$local_key_file --verbose --verbose",
1094 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1095 "$fwknopdCmd $default_server_conf_args $intf_str",
1100 'category' => 'Rijndael SPA',
1101 'subcategory' => 'client+server',
1102 'detail' => 'replay attack detection',
1103 'err_msg' => 'could not detect replay attack',
1104 'function' => \&replay_detection,
1105 'cmdline' => $default_client_args,
1106 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1107 "$fwknopdCmd $default_server_conf_args $intf_str",
1111 'category' => 'Rijndael SPA',
1112 'subcategory' => 'server',
1113 'detail' => 'digest cache structure',
1114 'err_msg' => 'improper digest cache structure',
1115 'function' => \&digest_cache_structure,
1120 'category' => 'Rijndael SPA',
1121 'subcategory' => 'client+server',
1122 'detail' => 'non-base64 altered SPA data',
1123 'err_msg' => 'allowed improper SPA data',
1124 'function' => \&altered_non_base64_spa_data,
1125 'cmdline' => $default_client_args,
1126 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1127 "$fwknopdCmd $default_server_conf_args $intf_str",
1131 'category' => 'Rijndael SPA',
1132 'subcategory' => 'client+server',
1133 'detail' => 'base64 altered SPA data',
1134 'err_msg' => 'allowed improper SPA data',
1135 'function' => \&altered_base64_spa_data,
1136 'cmdline' => $default_client_args,
1137 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1138 "$fwknopdCmd $default_server_conf_args $intf_str",
1142 'category' => 'Rijndael SPA',
1143 'subcategory' => 'client+server',
1144 'detail' => 'appended data to SPA pkt',
1145 'err_msg' => 'allowed improper SPA data',
1146 'function' => \&appended_spa_data,
1147 'cmdline' => $default_client_args,
1148 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1149 "$fwknopdCmd $default_server_conf_args $intf_str",
1153 'category' => 'Rijndael SPA',
1154 'subcategory' => 'client+server',
1155 'detail' => 'prepended data to SPA pkt',
1156 'err_msg' => 'allowed improper SPA data',
1157 'function' => \&prepended_spa_data,
1158 'cmdline' => $default_client_args,
1159 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1160 "$fwknopdCmd $default_server_conf_args $intf_str",
1165 'category' => 'GnuPG (GPG) SPA',
1166 'subcategory' => 'client+server',
1167 'detail' => 'complete cycle (tcp/22 ssh)',
1168 'err_msg' => 'could not complete SPA cycle',
1169 'function' => \&spa_cycle,
1170 'cmdline' => $default_client_gpg_args,
1171 'fwknopd_cmdline' => $default_server_gpg_args,
1172 'fw_rule_created' => $NEW_RULE_REQUIRED,
1173 'fw_rule_removed' => $NEW_RULE_REMOVED,
1177 'category' => 'GnuPG (GPG) SPA',
1178 'subcategory' => 'client+server',
1179 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1180 'err_msg' => 'could not complete SPA cycle',
1181 'function' => \&spa_cycle,
1182 'cmdline' => $default_client_gpg_args,
1183 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1184 "$valgrind_str $fwknopdCmd -c $default_conf " .
1185 "-a $multi_gpg_access_conf $intf_str " .
1186 "-d $default_digest_file -p $default_pid_file",
1187 'fw_rule_created' => $NEW_RULE_REQUIRED,
1188 'fw_rule_removed' => $NEW_RULE_REMOVED,
1193 'category' => 'GnuPG (GPG) SPA',
1194 'subcategory' => 'client+server',
1195 'detail' => 'complete cycle (tcp/23 telnet)',
1196 'err_msg' => 'could not complete SPA cycle',
1197 'function' => \&spa_cycle,
1198 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1199 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1200 "$local_key_file --verbose --verbose " .
1201 "--gpg-recipient-key $gpg_server_key " .
1202 "--gpg-signer-key $gpg_client_key " .
1203 "--gpg-home-dir $gpg_client_home_dir",
1204 'fwknopd_cmdline' => $default_server_gpg_args,
1205 'fw_rule_created' => $NEW_RULE_REQUIRED,
1206 'fw_rule_removed' => $NEW_RULE_REMOVED,
1210 'category' => 'GnuPG (GPG) SPA',
1211 'subcategory' => 'client+server',
1212 'detail' => 'complete cycle (tcp/9418 git)',
1213 'err_msg' => 'could not complete SPA cycle',
1214 'function' => \&spa_cycle,
1215 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1216 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1217 "$local_key_file --verbose --verbose " .
1218 "--gpg-recipient-key $gpg_server_key " .
1219 "--gpg-signer-key $gpg_client_key " .
1220 "--gpg-home-dir $gpg_client_home_dir",
1221 'fwknopd_cmdline' => $default_server_gpg_args,
1222 'fw_rule_created' => $NEW_RULE_REQUIRED,
1223 'fw_rule_removed' => $NEW_RULE_REMOVED,
1227 'category' => 'GnuPG (GPG) SPA',
1228 'subcategory' => 'client+server',
1229 'detail' => 'complete cycle (udp/53 dns)',
1230 'err_msg' => 'could not complete SPA cycle',
1231 'function' => \&spa_cycle,
1232 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1233 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1234 "$local_key_file --verbose --verbose " .
1235 "--gpg-recipient-key $gpg_server_key " .
1236 "--gpg-signer-key $gpg_client_key " .
1237 "--gpg-home-dir $gpg_client_home_dir",
1238 'fwknopd_cmdline' => $default_server_gpg_args,
1239 'fw_rule_created' => $NEW_RULE_REQUIRED,
1240 'fw_rule_removed' => $NEW_RULE_REMOVED,
1245 'category' => 'GnuPG (GPG) SPA',
1246 'subcategory' => 'client+server',
1247 'detail' => 'replay attack detection',
1248 'err_msg' => 'could not detect replay attack',
1249 'function' => \&replay_detection,
1250 'cmdline' => $default_client_gpg_args,
1251 'fwknopd_cmdline' => $default_server_gpg_args,
1256 'category' => 'GnuPG (GPG) SPA',
1257 'subcategory' => 'client+server',
1258 'detail' => 'non-base64 altered SPA data',
1259 'err_msg' => 'allowed improper SPA data',
1260 'function' => \&altered_non_base64_spa_data,
1261 'cmdline' => $default_client_gpg_args,
1262 'fwknopd_cmdline' => $default_server_gpg_args,
1266 'category' => 'GnuPG (GPG) SPA',
1267 'subcategory' => 'client+server',
1268 'detail' => 'base64 altered SPA data',
1269 'err_msg' => 'allowed improper SPA data',
1270 'function' => \&altered_base64_spa_data,
1271 'cmdline' => $default_client_gpg_args,
1272 'fwknopd_cmdline' => $default_server_gpg_args,
1276 'category' => 'GnuPG (GPG) SPA',
1277 'subcategory' => 'client+server',
1278 'detail' => 'appended data to SPA pkt',
1279 'err_msg' => 'allowed improper SPA data',
1280 'function' => \&appended_spa_data,
1281 'cmdline' => $default_client_gpg_args,
1282 'fwknopd_cmdline' => $default_server_gpg_args,
1286 'category' => 'GnuPG (GPG) SPA',
1287 'subcategory' => 'client+server',
1288 'detail' => 'prepended data to SPA pkt',
1289 'err_msg' => 'allowed improper SPA data',
1290 'function' => \&prepended_spa_data,
1291 'cmdline' => $default_client_gpg_args,
1292 'fwknopd_cmdline' => $default_server_gpg_args,
1296 'category' => 'GnuPG (GPG) SPA',
1297 'subcategory' => 'client+server',
1298 'detail' => 'spoof username (tcp/22 ssh)',
1299 'err_msg' => 'could not spoof username',
1300 'function' => \&spoof_username,
1301 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1302 'fwknopd_cmdline' => $default_server_gpg_args,
1307 'category' => 'GnuPG (GPG) SPA',
1308 'subcategory' => 'server',
1309 'detail' => 'digest cache structure',
1310 'err_msg' => 'improper digest cache structure',
1311 'function' => \&digest_cache_structure,
1316 'category' => 'profile coverage',
1317 'detail' => 'gcov profile coverage',
1318 'err_msg' => 'profile coverage failed',
1319 'function' => \&profile_coverage,
1325 'category' => $REQUIRED,
1326 'subcategory' => $OPTIONAL,
1327 'detail' => $REQUIRED,
1328 'function' => $REQUIRED,
1329 'binary' => $OPTIONAL,
1330 'cmdline' => $OPTIONAL,
1331 'fwknopd_cmdline' => $OPTIONAL,
1332 'fatal' => $OPTIONAL,
1333 'exec_err' => $OPTIONAL,
1334 'fw_rule_created' => $OPTIONAL,
1335 'fw_rule_removed' => $OPTIONAL,
1336 'server_conf' => $OPTIONAL,
1337 'positive_output_matches' => $OPTIONAL,
1338 'negative_output_matches' => $OPTIONAL,
1339 'server_positive_output_matches' => $OPTIONAL,
1340 'server_negative_output_matches' => $OPTIONAL,
1344 &diff_test_results();
1348 ### make sure everything looks as expected before continuing
1351 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1352 " args: @args_cp\n\n"
1355 ### save the results from any previous test suite run
1356 ### so that we can potentially compare them with --diff
1357 if ($saved_last_results) {
1358 &logr(" Saved results from previous run " .
1359 "to: ${output_dir}.last/\n\n");
1362 ### main loop through all of the tests
1363 for my $test_hr (@tests) {
1364 &run_test($test_hr);
1367 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1369 copy $logfile, "$output_dir/$logfile" or die $!;
1373 #===================== end main =======================
1376 my $test_hr = shift;
1378 my $msg = "[$test_hr->{'category'}]";
1379 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1380 $msg .= " $test_hr->{'detail'}";
1382 return unless &process_include_exclude($msg);
1392 $current_test_file = "$output_dir/$executed.test";
1393 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1395 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1396 $test_hr->{'msg'} = $msg;
1397 if (&{$test_hr->{'function'}}($test_hr)) {
1398 &logr("pass ($executed)\n");
1401 &logr("fail ($executed)\n");
1404 if ($test_hr->{'fatal'} eq $YES) {
1405 die "[*] required test failed, exiting.";
1412 sub process_include_exclude() {
1415 ### inclusions/exclusions
1416 if (@tests_to_include) {
1418 for my $test (@tests_to_include) {
1419 if ($msg =~ /$test/) {
1424 return 0 unless $found;
1426 if (@tests_to_exclude) {
1428 for my $test (@tests_to_exclude) {
1429 if ($msg =~ /$test/) {
1439 sub diff_test_results() {
1440 die "[*] Need results from a previous run before running --diff"
1441 unless -d "${output_dir}.last";
1442 die "[*] Current results set does not exist." unless -d $output_dir;
1444 my %current_tests = ();
1445 my %previous_tests = ();
1447 ### Only diff results for matching tests (parse the logfile to see which
1448 ### test numbers match across the two test cycles).
1449 &build_results_hash(\%current_tests, $output_dir);
1450 &build_results_hash(\%previous_tests, "${output_dir}.last");
1452 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1453 keys %current_tests) {
1454 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1455 my $current_num = $current_tests{$test_msg}{'num'};
1456 if (defined $previous_tests{$test_msg}) {
1457 print "[+] Checking: $test_msg\n";
1458 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1459 my $previous_num = $previous_tests{$test_msg}{'num'};
1460 if ($current_result ne $previous_result) {
1461 print " DIFF: **$current_result** $test_msg\n";
1464 &diff_results($previous_num, $current_num);
1472 sub diff_results() {
1473 my ($previous_num, $current_num) = @_;
1475 ### edit out any valgrind "==354==" prefixes
1476 my $valgrind_search_re = qr/^==\d+==\s/;
1478 ### remove CMD timestamps
1479 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1481 for my $file ("${output_dir}.last/${previous_num}.test",
1482 "${output_dir}.last/${previous_num}_fwknopd.test",
1483 "${output_dir}/${current_num}.test",
1484 "${output_dir}/${current_num}_fwknopd.test",
1486 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1487 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1490 if (-e "${output_dir}.last/${previous_num}.test"
1491 and -e "${output_dir}/${current_num}.test") {
1492 system "diff -u ${output_dir}.last/${previous_num}.test " .
1493 "${output_dir}/${current_num}.test";
1496 if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1497 and -e "${output_dir}/${current_num}_fwknopd.test") {
1498 system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1499 "${output_dir}/${current_num}_fwknopd.test";
1505 sub build_results_hash() {
1506 my ($hr, $dir) = @_;
1508 open F, "< $dir/$logfile" or die $!;
1510 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1511 $hr->{$1}{'pass_fail'} = $2;
1512 $hr->{$1}{'num'} = $3;
1518 sub compile_warnings() {
1520 ### 'make clean' as root
1521 return 0 unless &run_cmd('make -C .. clean',
1522 $cmd_out_tmp, $current_test_file);
1525 my $username = getpwuid((stat($configure_path))[4]);
1526 die "[*] Could not determine $configure_path owner"
1529 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1530 $cmd_out_tmp, $current_test_file);
1534 return 0 unless &run_cmd('make -C ..',
1535 $cmd_out_tmp, $current_test_file);
1539 ### look for compilation warnings - something like:
1540 ### warning: ‘test’ is used uninitialized in this function
1541 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
1542 $current_test_file);
1544 ### the new binaries should exist
1545 unless (-e $fwknopCmd and -x $fwknopCmd) {
1546 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1547 $current_test_file);
1549 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1550 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1551 $current_test_file);
1557 sub profile_coverage() {
1559 ### check for any *.gcno files - if they don't exist, then fwknop was
1560 ### not compiled with profile support
1561 unless (glob('../client/*.gcno') and glob('../server/*.gcno')) {
1562 &write_test_file("[-] ../client/*.gcno and " .
1563 "../server/*.gcno files do not exist.\n", $current_test_file);
1567 my $curr_dir = getcwd() or die $!;
1569 ### gcov -b ../client/*.gcno
1570 for my $dir ('../client', '../server', '../lib/.libs') {
1571 next unless -d $dir;
1572 chdir $dir or die $!;
1573 system "$gcov_path -b -u *.gcno > /dev/null 2>&1";
1574 chdir $curr_dir or die $!;
1576 &run_cmd(qq|grep "called 0 returned" $dir/*.gcov|,
1577 $cmd_out_tmp, $current_test_file);
1583 sub binary_exists() {
1584 my $test_hr = shift;
1585 return 0 unless $test_hr->{'binary'};
1587 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1588 ### libfko.so link on OpenBSD)
1590 if ($test_hr->{'binary'} =~ /libfko/) {
1591 unless (-e $test_hr->{'binary'}) {
1592 for my $file (glob("$lib_dir/libfko.so*")) {
1593 if (-e $file and -x $file) {
1594 $test_hr->{'binary'} = $file;
1595 $libfko_bin = $file;
1602 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1606 sub expected_code_version() {
1607 my $test_hr = shift;
1609 unless (-e '../VERSION') {
1610 &write_test_file("[-] ../VERSION file does not exist.\n",
1611 $current_test_file);
1615 open F, '< ../VERSION' or die $!;
1618 if ($line =~ /(\d.*\d)/) {
1620 return 0 unless &run_cmd($test_hr->{'cmdline'},
1621 $cmd_out_tmp, $current_test_file);
1622 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1627 sub client_send_spa_packet() {
1628 my $test_hr = shift;
1630 &write_key('fwknoptest', $local_key_file);
1632 return 0 unless &run_cmd($test_hr->{'cmdline'},
1633 $cmd_out_tmp, $current_test_file);
1634 return 0 unless &file_find_regex([qr/final\spacked/i],
1635 $current_test_file);
1641 my $test_hr = shift;
1643 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1644 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1646 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1647 $rv = 0 unless $fw_rule_created;
1648 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1649 $rv = 0 if $fw_rule_created;
1652 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1653 $rv = 0 unless $fw_rule_removed;
1654 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1655 $rv = 0 if $fw_rule_removed;
1658 if ($test_hr->{'server_positive_output_matches'}) {
1659 $rv = 0 unless &file_find_regex(
1660 $test_hr->{'server_positive_output_matches'},
1664 if ($test_hr->{'server_negative_output_matches'}) {
1665 $rv = 0 if &file_find_regex(
1666 $test_hr->{'server_negative_output_matches'},
1673 sub spoof_username() {
1674 my $test_hr = shift;
1676 my $rv = &spa_cycle($test_hr);
1678 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1679 $current_test_file)) {
1683 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1684 $server_test_file)) {
1691 sub replay_detection() {
1692 my $test_hr = shift;
1694 ### do a complete SPA cycle and then parse the SPA packet out of the
1695 ### current test file and re-send
1697 return 0 unless &spa_cycle($test_hr);
1699 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1702 &write_test_file("[-] could not get SPA packet " .
1703 "from file: $current_test_file\n",
1704 $current_test_file);
1711 'port' => $default_spa_port,
1712 'dst_ip' => $loopback_ip,
1717 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1718 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1720 $rv = 0 unless $server_was_stopped;
1722 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1723 $server_test_file)) {
1730 sub digest_cache_structure() {
1731 my $test_hr = shift;
1734 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1736 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1738 ### the format should be:
1739 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1740 open F, "< $default_digest_file" or
1741 die "[*] could not open $default_digest_file: $!";
1745 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1746 &write_test_file("[-] invalid digest.cache line: $_",
1747 $current_test_file);
1753 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1754 &write_test_file("[+] DBM digest file format, " .
1755 "assuming this is valid.\n", $current_test_file);
1757 ### don't know what kind of file the digest.cache is
1758 &write_test_file("[-] unrecognized file type for " .
1759 "$default_digest_file.\n", $current_test_file);
1764 &write_test_file("[+] valid digest.cache structure.\n",
1765 $current_test_file);
1771 sub server_bpf_ignore_packet() {
1772 my $test_hr = shift;
1775 my $server_was_stopped = 0;
1776 my $fw_rule_created = 0;
1777 my $fw_rule_removed = 0;
1779 unless (&client_send_spa_packet($test_hr)) {
1780 &write_test_file("[-] fwknop client execution error.\n",
1781 $current_test_file);
1785 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1788 &write_test_file("[-] could not get SPA packet " .
1789 "from file: $current_test_file\n", $current_test_file);
1796 'port' => $default_spa_port,
1797 'dst_ip' => $loopback_ip,
1802 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1803 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1805 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1806 $server_test_file)) {
1813 sub altered_non_base64_spa_data() {
1814 my $test_hr = shift;
1817 my $server_was_stopped = 0;
1818 my $fw_rule_created = 0;
1819 my $fw_rule_removed = 0;
1821 unless (&client_send_spa_packet($test_hr)) {
1822 &write_test_file("[-] fwknop client execution error.\n",
1823 $current_test_file);
1827 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1830 &write_test_file("[-] could not get SPA packet " .
1831 "from file: $current_test_file\n", $current_test_file);
1835 ### alter one byte (change to a ":")
1836 $spa_pkt =~ s|^(.{3}).|$1:|;
1841 'port' => $default_spa_port,
1842 'dst_ip' => $loopback_ip,
1847 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1848 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1850 $rv = 0 unless $server_was_stopped;
1855 sub altered_base64_spa_data() {
1856 my $test_hr = shift;
1859 my $server_was_stopped = 0;
1860 my $fw_rule_created = 0;
1861 my $fw_rule_removed = 0;
1863 unless (&client_send_spa_packet($test_hr)) {
1864 &write_test_file("[-] fwknop client execution error.\n",
1865 $current_test_file);
1869 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1872 &write_test_file("[-] could not get SPA packet " .
1873 "from file: $current_test_file\n", $current_test_file);
1877 $spa_pkt =~ s|^(.{3}).|AAAA|;
1882 'port' => $default_spa_port,
1883 'dst_ip' => $loopback_ip,
1888 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1889 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1891 $rv = 0 unless $server_was_stopped;
1893 if ($fw_rule_created) {
1894 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1897 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1900 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1901 $server_test_file)) {
1908 sub appended_spa_data() {
1909 my $test_hr = shift;
1912 my $server_was_stopped = 0;
1913 my $fw_rule_created = 0;
1914 my $fw_rule_removed = 0;
1916 unless (&client_send_spa_packet($test_hr)) {
1917 &write_test_file("[-] fwknop client execution error.\n",
1918 $current_test_file);
1922 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1925 &write_test_file("[-] could not get SPA packet " .
1926 "from file: $current_test_file\n", $current_test_file);
1935 'port' => $default_spa_port,
1936 'dst_ip' => $loopback_ip,
1941 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1942 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1944 $rv = 0 unless $server_was_stopped;
1946 if ($fw_rule_created) {
1947 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1950 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1953 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1954 $server_test_file)) {
1961 sub prepended_spa_data() {
1962 my $test_hr = shift;
1965 my $server_was_stopped = 0;
1966 my $fw_rule_created = 0;
1967 my $fw_rule_removed = 0;
1969 unless (&client_send_spa_packet($test_hr)) {
1970 &write_test_file("[-] fwknop client execution error.\n",
1971 $current_test_file);
1975 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1978 &write_test_file("[-] could not get SPA packet " .
1979 "from file: $current_test_file\n", $current_test_file);
1983 $spa_pkt = 'AAAA' . $spa_pkt;
1988 'port' => $default_spa_port,
1989 'dst_ip' => $loopback_ip,
1994 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1995 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1997 $rv = 0 unless $server_was_stopped;
1999 if ($fw_rule_created) {
2000 &write_test_file("[-] new fw rule created.\n", $current_test_file);
2003 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
2006 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
2007 $server_test_file)) {
2014 sub server_start() {
2015 my $test_hr = shift;
2017 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2018 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2020 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
2021 $server_test_file)) {
2025 $rv = 0 unless $server_was_stopped;
2031 my $test_hr = shift;
2033 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2034 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2036 $rv = 0 unless $server_was_stopped;
2041 sub server_packet_limit() {
2042 my $test_hr = shift;
2047 'port' => $default_spa_port,
2048 'dst_ip' => $loopback_ip,
2053 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2054 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2056 if (&is_fwknopd_running()) {
2061 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2062 $server_test_file)) {
2066 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2067 $server_test_file)) {
2074 sub server_ignore_small_packets() {
2075 my $test_hr = shift;
2080 'port' => $default_spa_port,
2081 'dst_ip' => $loopback_ip,
2082 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2086 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2087 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2091 if (&is_fwknopd_running()) {
2099 sub client_server_interaction() {
2100 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2103 my $server_was_stopped = 1;
2104 my $fw_rule_created = 1;
2105 my $fw_rule_removed = 0;
2107 ### start fwknopd to monitor for the SPA packet over the loopback interface
2108 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2110 ### give fwknopd a chance to parse its config and start sniffing
2111 ### on the loopback interface
2112 if ($use_valgrind) {
2118 ### send the SPA packet(s) to the server either manually using IO::Socket or
2119 ### with the fwknopd client
2120 if ($spa_client_flag == $USE_CLIENT) {
2121 unless (&client_send_spa_packet($test_hr)) {
2122 &write_test_file("[-] fwknop client execution error.\n",
2123 $current_test_file);
2127 &send_packets($pkts_hr);
2130 ### check to see if the SPA packet resulted in a new fw access rule
2132 while (not &is_fw_rule_active($test_hr)) {
2133 &write_test_file("[-] new fw rule does not exist.\n",
2134 $current_test_file);
2140 $fw_rule_created = 0;
2141 $fw_rule_removed = 0;
2144 &time_for_valgrind() if $use_valgrind;
2146 if ($fw_rule_created) {
2147 sleep 3; ### allow time for rule time out.
2148 if (&is_fw_rule_active($test_hr)) {
2149 &write_test_file("[-] new fw rule not timed out.\n",
2150 $current_test_file);
2153 &write_test_file("[+] new fw rule timed out.\n",
2154 $current_test_file);
2155 $fw_rule_removed = 1;
2159 if (&is_fwknopd_running()) {
2161 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2162 $server_test_file)) {
2163 $server_was_stopped = 0;
2166 &write_test_file("[-] server is not running.\n",
2167 $current_test_file);
2168 $server_was_stopped = 0;
2171 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2174 sub get_spa_packet_from_file() {
2179 my $found_trigger_line = 0;
2180 open F, "< $file" or die "[*] Could not open file $file: $!";
2182 if (/final\spacked/i) {
2183 $found_trigger_line = 1;
2186 next unless $found_trigger_line;
2188 ### the next line with non whitespace is the SPA packet
2199 sub send_packets() {
2200 my $pkts_ar = shift;
2202 open F, ">> $current_test_file" or die $!;
2203 print F "[+] send_packets(): Sending the following packets...\n";
2204 print F Dumper $pkts_ar;
2207 for my $pkt_hr (@$pkts_ar) {
2208 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2209 my $socket = IO::Socket::INET->new(
2210 PeerAddr => $pkt_hr->{'dst_ip'},
2211 PeerPort => $pkt_hr->{'port'},
2212 Proto => $pkt_hr->{'proto'},
2214 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2215 "socket to $pkt_hr->{'dst_ip'}: $!";
2217 $socket->send($pkt_hr->{'data'});
2220 } elsif ($pkt_hr->{'proto'} eq 'http') {
2222 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2226 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2231 sub generic_exec() {
2232 my $test_hr = shift;
2236 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2237 $cmd_out_tmp, $current_test_file);
2239 if ($test_hr->{'exec_err'} eq $YES) {
2240 $rv = 0 if $exec_rv;
2242 $rv = 0 unless $exec_rv;
2245 if ($test_hr->{'positive_output_matches'}) {
2246 $rv = 0 unless &file_find_regex(
2247 $test_hr->{'positive_output_matches'},
2248 $current_test_file);
2251 if ($test_hr->{'negative_output_matches'}) {
2252 $rv = 0 if &file_find_regex(
2253 $test_hr->{'negative_output_matches'},
2254 $current_test_file);
2262 my $test_hr = shift;
2263 return 0 unless $test_hr->{'binary'};
2264 &run_cmd("./hardening-check $test_hr->{'binary'}",
2265 $cmd_out_tmp, $current_test_file);
2266 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2267 $current_test_file);
2271 ### check for stack protection
2272 sub stack_protected_binary() {
2273 my $test_hr = shift;
2274 return 0 unless $test_hr->{'binary'};
2275 &run_cmd("./hardening-check $test_hr->{'binary'}",
2276 $cmd_out_tmp, $current_test_file);
2277 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2278 $current_test_file);
2282 ### check for fortified source functions
2283 sub fortify_source_functions() {
2284 my $test_hr = shift;
2285 return 0 unless $test_hr->{'binary'};
2286 &run_cmd("./hardening-check $test_hr->{'binary'}",
2287 $cmd_out_tmp, $current_test_file);
2288 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2289 $current_test_file);
2293 ### check for read-only relocations
2294 sub read_only_relocations() {
2295 my $test_hr = shift;
2296 return 0 unless $test_hr->{'binary'};
2297 &run_cmd("./hardening-check $test_hr->{'binary'}",
2298 $cmd_out_tmp, $current_test_file);
2299 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2300 $current_test_file);
2304 ### check for immediate binding
2305 sub immediate_binding() {
2306 my $test_hr = shift;
2307 return 0 unless $test_hr->{'binary'};
2308 &run_cmd("./hardening-check $test_hr->{'binary'}",
2309 $cmd_out_tmp, $current_test_file);
2310 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2311 $current_test_file);
2317 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2318 "$default_server_conf_args --fw-list-all",
2319 $cmd_out_tmp, $current_test_file);
2327 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2328 'if [ `which iptables` ]; then iptables -V; fi',
2329 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2330 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2331 'if [ `which gpg` ]; then gpg --version; fi',
2332 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2336 'ls -l /usr/lib/*pcap*',
2337 'ls -l /usr/local/lib/*pcap*',
2338 'ls -l /usr/lib/*fko*',
2339 'ls -l /usr/local/lib/*fko*',
2341 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2343 if ($cmd =~ /^ldd/) {
2344 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2348 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2349 ### to enable gpg tests
2350 unless ($have_gpgme == 3) {
2351 push @tests_to_exclude, "GPG";
2357 sub time_for_valgrind() {
2359 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2360 "grep valgrind |grep -v perl | grep -v grep",
2361 $cmd_out_tmp, $current_test_file)) {
2369 sub anonymize_results() {
2371 die "[*] $output_dir does not exist" unless -d $output_dir;
2372 die "[*] $logfile does not exist, has $0 been executed?"
2375 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2378 ### remove non-loopback IP addresses
2379 my $search_re = qr/\b127\.0\.0\.1\b/;
2380 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2381 $search_re = qr/\b127\.0\.0\.2\b/;
2382 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2383 $search_re = qr/\b0\.0\.0\.0\b/;
2384 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2385 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2386 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2387 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2388 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2389 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2391 ### remove hostname from any uname output
2392 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2393 system "perl -p -i -e 'undef \$/; s|$search_re" .
2394 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2396 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2397 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2400 system "tar cvfz $tarfile $logfile $output_dir";
2401 print "[+] Anonymized test results file: $tarfile\n";
2410 my $test_hr = shift;
2412 open F, "> $default_pid_file" or die $!;
2416 &server_start($test_hr);
2418 open F, "< $default_pid_file" or die $!;
2430 sub start_fwknopd() {
2431 my $test_hr = shift;
2433 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2436 die "[*] Could not fork: $!" unless defined $pid;
2440 ### we are the child, so start fwknopd
2441 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2442 $server_cmd_tmp, $server_test_file);
2448 my ($key, $file) = @_;
2450 open K, "> $file" or die "[*] Could not open $file: $!";
2451 print K "$loopback_ip: $key\n";
2452 print K "localhost: $key\n";
2453 print K "some.host.through.proxy.com: $key\n";
2459 open C, ">> $current_test_file"
2460 or die "[*] Could not open $current_test_file: $!";
2461 print C "\n" . localtime() . " [+] PID dump:\n";
2463 &run_cmd("ps auxww | grep knop |grep -v grep",
2464 $cmd_out_tmp, $current_test_file);
2469 my ($cmd, $cmd_out, $file) = @_;
2473 or die "[*] Could not open $file: $!";
2474 print F localtime() . " CMD: $cmd\n";
2478 or die "[*] Could not open $file: $!";
2479 print F localtime() . " CMD: $cmd\n";
2483 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2485 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2486 my @cmd_lines = <C>;
2489 open F, ">> $file" or die "[*] Could not open $file: $!";
2490 print F $_ for @cmd_lines;
2503 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2512 $|++; ### turn off buffering
2514 $< == 0 && $> == 0 or
2515 die "[*] $0: You must be root (or equivalent ",
2516 "UID 0 account) to effectively test fwknop";
2518 ### validate test hashes
2520 for my $test_hr (@tests) {
2521 for my $key (keys %test_keys) {
2522 if ($test_keys{$key} == $REQUIRED) {
2523 die "[*] Missing '$key' element in hash: $hash_num"
2524 unless defined $test_hr->{$key};
2526 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2532 if ($use_valgrind) {
2533 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2534 unless -e $valgrindCmd and -x $valgrindCmd;
2537 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2538 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2540 for my $file ($configure_path,
2543 $default_access_conf,
2544 $no_source_match_access_conf,
2545 $ip_source_match_access_conf,
2546 $subnet_source_match_access_conf,
2547 $no_subnet_source_match_access_conf,
2548 $no_multi_source_match_access_conf,
2549 $multi_source_match_access_conf,
2550 $open_ports_access_conf,
2551 $mismatch_open_ports_access_conf,
2552 $require_user_access_conf,
2553 $mismatch_user_access_conf,
2554 $require_src_access_conf,
2555 $multi_gpg_access_conf,
2556 $multi_stanzas_access_conf,
2557 $expired_access_conf,
2558 $expired_epoch_access_conf,
2559 $future_expired_access_conf,
2560 $invalid_expire_access_conf,
2561 $force_nat_access_conf,
2563 die "[*] $file does not exist" unless -e $file;
2566 if (-d $output_dir) {
2567 if (-d "${output_dir}.last") {
2568 rmtree "${output_dir}.last"
2569 or die "[*] rmtree ${output_dir}.last $!";
2571 mkdir "${output_dir}.last"
2572 or die "[*] ${output_dir}.last: $!";
2573 for my $file (glob("$output_dir/*.test")) {
2574 if ($file =~ m|.*/(.*)|) {
2575 copy $file, "${output_dir}.last/$1" or die $!;
2578 if (-e "$output_dir/init") {
2579 copy "$output_dir/init", "${output_dir}.last/init";
2582 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2584 $saved_last_results = 1;
2586 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2588 unless (-d $run_dir) {
2589 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2592 for my $file (glob("$output_dir/*.test")) {
2593 unlink $file or die "[*] Could not unlink($file)";
2595 if (-e "$output_dir/init") {
2596 unlink "$output_dir/init" or die $!;
2600 unlink $logfile or die $!;
2603 if ($test_include) {
2604 @tests_to_include = split /\s*,\s*/, $test_include;
2606 if ($test_exclude) {
2607 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2610 ### make sure no fwknopd instance is currently running
2611 die "[*] Please stop the running fwknopd instance."
2612 if &is_fwknopd_running();
2614 unless ($enable_recompilation_warnings_check) {
2615 push @tests_to_exclude, 'recompilation';
2618 unless ($enable_profile_coverage_check) {
2619 push @tests_to_exclude, 'profile coverage';
2622 $sudo_path = &find_command('sudo');
2624 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2625 ### disable compilation checks
2626 push @tests_to_exclude, 'recompilation';
2629 $gcov_path = &find_command('gcov');
2632 if ($enable_profile_coverage_check) {
2633 for my $extension ('*.gcov', '*.gcda') {
2634 ### remove profile output from any previous run
2635 system qq{find .. -name $extension | xargs rm 2> /dev/null};
2639 push @tests_to_exclude, 'profile coverage';
2642 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2645 $platform = 'linux';
2651 unless ($platform eq 'linux') {
2652 push @tests_to_exclude, 'NAT';
2658 sub identify_loopback_intf() {
2659 return if $loopback_intf;
2663 ### lo Link encap:Local Loopback
2664 ### inet addr:127.0.0.1 Mask:255.0.0.0
2665 ### inet6 addr: ::1/128 Scope:Host
2666 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2667 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2668 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2669 ### collisions:0 txqueuelen:0
2670 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2674 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2675 ### options=3<RXCSUM,TXCSUM>
2676 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2677 ### inet6 ::1 prefixlen 128
2678 ### inet 127.0.0.1 netmask 0xff000000
2679 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2682 my $found_loopback_intf = 0;
2684 my $cmd = 'ifconfig -a';
2685 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2687 if (/^(\S+?):?\s+.*loopback/i) {
2691 if (/^\S/ and $intf and not $found_loopback_intf) {
2692 ### should not happen
2695 if ($intf and /\b127\.0\.0\.1\b/) {
2696 $found_loopback_intf = 1;
2702 die "[*] could not determine loopback interface, use --loopback <name>"
2703 unless $found_loopback_intf;
2705 $loopback_intf = $intf;
2710 sub is_fw_rule_active() {
2711 my $test_hr = shift;
2713 my $conf_args = $default_server_conf_args;
2715 if ($test_hr->{'server_conf'}) {
2716 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2717 "-d $default_digest_file -p $default_pid_file";
2720 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2721 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2722 $cmd_out_tmp, $current_test_file);
2726 sub is_fwknopd_running() {
2728 sleep 2 if $use_valgrind;
2730 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2731 "--status", $cmd_out_tmp, $current_test_file);
2733 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2738 sub stop_fwknopd() {
2740 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2741 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2743 if ($use_valgrind) {
2744 &time_for_valgrind();
2752 sub file_find_regex() {
2753 my ($re_ar, $file) = @_;
2756 my @write_lines = ();
2758 open F, "< $file" or die "[*] Could not open $file: $!";
2761 next LINE if $line =~ /file_file_regex\(\)/;
2762 for my $re (@$re_ar) {
2764 push @write_lines, "[.] file_find_regex() " .
2765 "Matched '$re' with line: $line";
2774 for my $line (@write_lines) {
2775 &write_test_file($line, $file);
2778 &write_test_file("[.] find_find_regex() Did not " .
2779 "match any regex in: '@$re_ar'\n", $file);
2785 sub find_command() {
2789 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2791 if (m|^(/.*$cmd)$|) {
2800 sub write_test_file() {
2801 my ($msg, $file) = @_;
2805 or die "[*] Could not open $file: $!";
2810 or die "[*] Could not open $file: $!";
2820 open F, ">> $logfile" or die $!;