7 use Getopt::Long 'GetOptions';
10 #==================== config =====================
11 my $logfile = 'test.log';
12 my $local_key_file = 'local_spa.key';
13 my $output_dir = 'output';
14 my $lib_dir = '../lib/.libs';
15 my $conf_dir = 'conf';
17 my $configure_path = '../configure';
18 my $cmd_out_tmp = 'cmd.out';
19 my $server_cmd_tmp = 'server_cmd.out';
20 my $gpg_client_home_dir = "$conf_dir/client-gpg";
22 my $nat_conf = "$conf_dir/nat_fwknopd.conf";
23 my $default_conf = "$conf_dir/default_fwknopd.conf";
24 my $default_access_conf = "$conf_dir/default_access.conf";
25 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
26 my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
27 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
28 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
29 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
30 my $local_nat_fwknopd_conf = "$conf_dir/local_nat_fwknopd.conf";
31 my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
32 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
33 my $default_digest_file = "$run_dir/digest.cache";
34 my $default_pid_file = "$run_dir/fwknopd.pid";
35 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
36 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
37 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
38 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
39 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
40 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
41 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
42 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
43 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
44 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
45 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
46 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
47 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
48 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
50 my $fwknopCmd = '../client/.libs/fwknop';
51 my $fwknopdCmd = '../server/.libs/fwknopd';
52 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
53 my $valgrindCmd = '/usr/bin/valgrind';
55 my $gpg_server_key = '361BBAD4';
56 my $gpg_client_key = '6A3FAD56';
58 my $loopback_ip = '127.0.0.1';
59 my $fake_ip = '127.0.0.2';
60 my $internal_nat_host = '192.168.1.2';
61 my $force_nat_host = '192.168.1.123';
62 my $default_spa_port = 62201;
63 my $non_std_spa_port = 12345;
65 my $spoof_user = 'testuser';
66 #================== end config ===================
71 my $test_include = '';
72 my @tests_to_include = ();
73 my $test_exclude = '';
74 my @tests_to_exclude = ();
75 my %valgrind_flagged_fcns = ();
76 my %valgrind_flagged_fcns_unique = ();
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;
96 my $USE_PREDEF_PKTS = 1;
100 my $NEW_RULE_REQUIRED = 1;
101 my $REQUIRE_NO_NEW_RULE = 2;
102 my $NEW_RULE_REMOVED = 1;
103 my $REQUIRE_NO_NEW_REMOVED = 2;
105 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
109 exit 1 unless GetOptions(
110 'Anonymize-results' => \$anonymize_results,
111 'fwknop-path=s' => \$fwknopCmd,
112 'fwknopd-path=s' => \$fwknopdCmd,
113 'libfko-path=s' => \$libfko_bin,
114 'loopback-intf=s' => \$loopback_intf,
115 'test-include=s' => \$test_include,
116 'include=s' => \$test_include, ### synonym
117 'test-exclude=s' => \$test_exclude,
118 'exclude=s' => \$test_exclude, ### synonym
119 'enable-recompile-check' => \$enable_recompilation_warnings_check,
120 'List-mode' => \$list_mode,
121 'enable-valgrind' => \$use_valgrind,
122 'valgrind-path=s' => \$valgrindCmd,
123 'output-dir=s' => \$output_dir,
124 'diff' => \$diff_mode,
125 'diff-dir1=s' => \$diff_dir1,
126 'diff-dir2=s' => \$diff_dir2,
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' => 'dual usage access key (tcp/80 http)',
606 'err_msg' => 'could not complete SPA cycle',
607 'function' => \&spa_cycle,
608 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
609 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
610 "$local_key_file --verbose --verbose",
611 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
612 "$fwknopdCmd -c $default_conf -a $dual_key_usage_access_conf " .
613 "-d $default_digest_file -p $default_pid_file $intf_str",
614 ### check for the first stanza that does not allow tcp/80 - the
615 ### second stanza allows this
616 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
617 'fw_rule_created' => $NEW_RULE_REQUIRED,
618 'fw_rule_removed' => $NEW_RULE_REMOVED,
622 'category' => 'Rijndael SPA',
623 'subcategory' => 'client+server',
624 'detail' => 'packet aging (past) (tcp/22 ssh)',
625 'err_msg' => 'old SPA packet accepted',
626 'function' => \&spa_cycle,
627 'cmdline' => "$default_client_args --time-offset-minus 300s",
628 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
629 "$fwknopdCmd $default_server_conf_args $intf_str",
630 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
631 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
635 'category' => 'Rijndael SPA',
636 'subcategory' => 'client+server',
637 'detail' => 'packet aging (future) (tcp/22 ssh)',
638 'err_msg' => 'future SPA packet accepted',
639 'function' => \&spa_cycle,
640 'cmdline' => "$default_client_args --time-offset-plus 300s",
641 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
642 "$fwknopdCmd $default_server_conf_args $intf_str",
643 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
644 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
648 'category' => 'Rijndael SPA',
649 'subcategory' => 'client+server',
650 'detail' => 'expired stanza (tcp/22 ssh)',
651 'err_msg' => 'SPA packet accepted',
652 'function' => \&spa_cycle,
653 'cmdline' => $default_client_args,
654 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
655 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
656 "-d $default_digest_file -p $default_pid_file $intf_str",
657 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
658 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
662 'category' => 'Rijndael SPA',
663 'subcategory' => 'client+server',
664 'detail' => 'invalid expire date (tcp/22 ssh)',
665 'err_msg' => 'SPA packet accepted',
666 'function' => \&spa_cycle,
667 'cmdline' => $default_client_args,
668 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
669 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
670 "-d $default_digest_file -p $default_pid_file $intf_str",
671 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
672 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
676 'category' => 'Rijndael SPA',
677 'subcategory' => 'client+server',
678 'detail' => 'expired epoch stanza (tcp/22 ssh)',
679 'err_msg' => 'SPA packet accepted',
680 'function' => \&spa_cycle,
681 'cmdline' => $default_client_args,
682 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
683 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
684 "-d $default_digest_file -p $default_pid_file $intf_str",
685 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
686 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
690 'category' => 'Rijndael SPA',
691 'subcategory' => 'client+server',
692 'detail' => 'future expired stanza (tcp/22 ssh)',
693 'err_msg' => 'SPA packet not accepted',
694 'function' => \&spa_cycle,
695 'cmdline' => $default_client_args,
696 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
697 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
698 "-d $default_digest_file -p $default_pid_file $intf_str",
699 'fw_rule_created' => $NEW_RULE_REQUIRED,
700 'fw_rule_removed' => $NEW_RULE_REMOVED,
705 'category' => 'Rijndael SPA',
706 'subcategory' => 'client+server',
707 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
708 'err_msg' => "improper OPEN_PORTS result",
709 'function' => \&spa_cycle,
710 'cmdline' => $default_client_args,
711 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
712 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
713 "-d $default_digest_file -p $default_pid_file $intf_str",
714 'fw_rule_created' => $NEW_RULE_REQUIRED,
715 'fw_rule_removed' => $NEW_RULE_REMOVED,
719 'category' => 'Rijndael SPA',
720 'subcategory' => 'client+server',
721 'detail' => 'OPEN_PORTS mismatch',
722 'err_msg' => "SPA packet accepted",
723 'function' => \&spa_cycle,
724 'cmdline' => $default_client_args,
725 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
726 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
727 "-d $default_digest_file -p $default_pid_file $intf_str",
728 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
729 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
733 'category' => 'Rijndael SPA',
734 'subcategory' => 'client+server',
735 'detail' => 'require user (tcp/22 ssh)',
736 'err_msg' => "missed require user criteria",
737 'function' => \&spa_cycle,
738 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
739 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
740 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
741 "-d $default_digest_file -p $default_pid_file $intf_str",
742 'fw_rule_created' => $NEW_RULE_REQUIRED,
743 'fw_rule_removed' => $NEW_RULE_REMOVED,
747 'category' => 'Rijndael SPA',
748 'subcategory' => 'client+server',
749 'detail' => 'user mismatch (tcp/22 ssh)',
750 'err_msg' => "improper user accepted for access",
751 'function' => \&user_mismatch,
752 'function' => \&spa_cycle,
753 'cmdline' => $default_client_args,
754 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
755 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
756 "-d $default_digest_file -p $default_pid_file $intf_str",
757 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
758 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
762 'category' => 'Rijndael SPA',
763 'subcategory' => 'client+server',
764 'detail' => 'require src (tcp/22 ssh)',
765 'err_msg' => "fw rule not created",
766 'function' => \&spa_cycle,
767 'cmdline' => $default_client_args,
768 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
769 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
770 "-d $default_digest_file -p $default_pid_file $intf_str",
771 'fw_rule_created' => $NEW_RULE_REQUIRED,
772 'fw_rule_removed' => $NEW_RULE_REMOVED,
776 'category' => 'Rijndael SPA',
777 'subcategory' => 'client+server',
778 'detail' => 'mismatch require src (tcp/22 ssh)',
779 'err_msg' => "fw rule created",
780 'function' => \&spa_cycle,
781 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
782 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
783 "$local_key_file --verbose --verbose",
784 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
785 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
786 "-d $default_digest_file -p $default_pid_file $intf_str",
787 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
788 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
793 'category' => 'Rijndael SPA',
794 'subcategory' => 'client+server',
795 'detail' => 'IP filtering (tcp/22 ssh)',
796 'err_msg' => "did not filter $loopback_ip",
797 'function' => \&spa_cycle,
798 'cmdline' => $default_client_args,
799 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
800 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
801 "-d $default_digest_file -p $default_pid_file $intf_str",
802 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
803 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
807 'category' => 'Rijndael SPA',
808 'subcategory' => 'client+server',
809 'detail' => 'subnet filtering (tcp/22 ssh)',
810 'err_msg' => "did not filter $loopback_ip",
811 'function' => \&spa_cycle,
812 'cmdline' => $default_client_args,
813 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
814 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
815 "-d $default_digest_file -p $default_pid_file $intf_str",
816 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
817 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
821 'category' => 'Rijndael SPA',
822 'subcategory' => 'client+server',
823 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
824 'err_msg' => "did not filter $loopback_ip",
825 'function' => \&spa_cycle,
826 'cmdline' => $default_client_args,
827 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
828 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
829 "-d $default_digest_file -p $default_pid_file $intf_str",
830 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
831 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
835 'category' => 'Rijndael SPA',
836 'subcategory' => 'client+server',
837 'detail' => 'IP match (tcp/22 ssh)',
838 'err_msg' => "did not filter $loopback_ip",
839 'function' => \&spa_cycle,
840 'cmdline' => $default_client_args,
841 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
842 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
843 "-d $default_digest_file -p $default_pid_file $intf_str",
844 'fw_rule_created' => $NEW_RULE_REQUIRED,
845 'fw_rule_removed' => $NEW_RULE_REMOVED,
849 'category' => 'Rijndael SPA',
850 'subcategory' => 'client+server',
851 'detail' => 'subnet match (tcp/22 ssh)',
852 'err_msg' => "did not filter $loopback_ip",
853 'function' => \&spa_cycle,
854 'cmdline' => $default_client_args,
855 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
856 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
857 "-d $default_digest_file -p $default_pid_file $intf_str",
858 'fw_rule_created' => $NEW_RULE_REQUIRED,
859 'fw_rule_removed' => $NEW_RULE_REMOVED,
863 'category' => 'Rijndael SPA',
864 'subcategory' => 'client+server',
865 'detail' => 'multi IP/net match (tcp/22 ssh)',
866 'err_msg' => "did not filter $loopback_ip",
867 'function' => \&spa_cycle,
868 'cmdline' => $default_client_args,
869 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
870 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
871 "-d $default_digest_file -p $default_pid_file $intf_str",
872 'fw_rule_created' => $NEW_RULE_REQUIRED,
873 'fw_rule_removed' => $NEW_RULE_REMOVED,
877 'category' => 'Rijndael SPA',
878 'subcategory' => 'client+server',
879 'detail' => 'multi access stanzas (tcp/22 ssh)',
880 'err_msg' => "could not complete SPA cycle",
881 'function' => \&spa_cycle,
882 'cmdline' => $default_client_args,
883 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
884 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
885 "-d $default_digest_file -p $default_pid_file $intf_str",
886 'fw_rule_created' => $NEW_RULE_REQUIRED,
887 'fw_rule_removed' => $NEW_RULE_REMOVED,
891 'category' => 'Rijndael SPA',
892 'subcategory' => 'client+server',
893 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
894 'err_msg' => "could not complete SPA cycle",
895 'function' => \&spa_cycle,
896 'cmdline' => $default_client_args,
897 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
898 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
899 "-d $default_digest_file -p $default_pid_file $intf_str",
900 'fw_rule_created' => $NEW_RULE_REQUIRED,
901 'fw_rule_removed' => $NEW_RULE_REMOVED,
906 'category' => 'Rijndael SPA',
907 'subcategory' => 'client+server',
908 'detail' => "non-enabled NAT (tcp/22 ssh)",
909 'err_msg' => "SPA packet not filtered",
910 'function' => \&spa_cycle,
911 'cmdline' => "$default_client_args -N $internal_nat_host:22",
912 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
913 "$fwknopdCmd $default_server_conf_args $intf_str",
914 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
915 'server_conf' => $nat_conf,
916 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
920 'category' => 'Rijndael SPA',
921 'subcategory' => 'client+server',
922 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
923 'err_msg' => "could not complete NAT SPA cycle",
924 'function' => \&spa_cycle,
925 'cmdline' => "$default_client_args -N $internal_nat_host:22",
926 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
927 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
928 "-d $default_digest_file -p $default_pid_file $intf_str",
929 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
930 'fw_rule_created' => $NEW_RULE_REQUIRED,
931 'fw_rule_removed' => $NEW_RULE_REMOVED,
932 'server_conf' => $nat_conf,
936 'category' => 'Rijndael SPA',
937 'subcategory' => 'client+server',
938 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
939 'err_msg' => "could not complete NAT SPA cycle",
940 'function' => \&spa_cycle,
941 'cmdline' => $default_client_args,
942 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
943 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
944 "-d $default_digest_file -p $default_pid_file $intf_str",
945 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
946 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
947 'fw_rule_created' => $NEW_RULE_REQUIRED,
948 'fw_rule_removed' => $NEW_RULE_REMOVED,
949 'server_conf' => $nat_conf,
953 'category' => 'Rijndael SPA',
954 'subcategory' => 'client+server',
955 'detail' => "local NAT $force_nat_host (tcp/22 ssh)",
956 'err_msg' => "could not complete NAT SPA cycle",
957 'function' => \&spa_cycle,
958 'cmdline' => "$default_client_args --nat-local",
959 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
960 "$fwknopdCmd -c $local_nat_fwknopd_conf -a $force_nat_access_conf " .
961 "-d $default_digest_file -p $default_pid_file $intf_str",
962 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i,
963 qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
964 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
965 'fw_rule_created' => $NEW_RULE_REQUIRED,
966 'fw_rule_removed' => $NEW_RULE_REMOVED,
967 'server_conf' => $nat_conf,
971 'category' => 'Rijndael SPA',
972 'subcategory' => 'client+server',
973 'detail' => "local NAT non-FORCE_NAT (tcp/22 ssh)",
974 'err_msg' => "could not complete NAT SPA cycle",
975 'function' => \&spa_cycle,
976 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
977 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
978 "$local_key_file --verbose --verbose --nat-local --nat-port 22",
979 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
980 "$fwknopdCmd -c $local_nat_fwknopd_conf -a $default_access_conf " .
981 "-d $default_digest_file -p $default_pid_file $intf_str",
982 'server_positive_output_matches' => [qr/to\:$fake_ip\:22/i,
983 qr/FWKNOP_INPUT.*dport\s22.*\sACCEPT/],
984 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
985 'fw_rule_created' => $NEW_RULE_REQUIRED,
986 'fw_rule_removed' => $NEW_RULE_REMOVED,
987 'server_conf' => $nat_conf,
992 'category' => 'Rijndael SPA',
993 'subcategory' => 'client+server',
994 'detail' => 'complete cycle (tcp/23 telnet)',
995 'err_msg' => 'could not complete SPA cycle',
996 'function' => \&spa_cycle,
997 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
998 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
999 "$local_key_file --verbose --verbose",
1000 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1001 "$fwknopdCmd $default_server_conf_args $intf_str",
1002 'fw_rule_created' => $NEW_RULE_REQUIRED,
1003 'fw_rule_removed' => $NEW_RULE_REMOVED,
1007 'category' => 'Rijndael SPA',
1008 'subcategory' => 'client+server',
1009 'detail' => 'complete cycle (tcp/9418 git)',
1010 'err_msg' => 'could not complete SPA cycle',
1011 'function' => \&spa_cycle,
1012 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1013 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1014 "$local_key_file --verbose --verbose",
1015 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1016 "$fwknopdCmd $default_server_conf_args $intf_str",
1017 'fw_rule_created' => $NEW_RULE_REQUIRED,
1018 'fw_rule_removed' => $NEW_RULE_REMOVED,
1022 'category' => 'Rijndael SPA',
1023 'subcategory' => 'client+server',
1024 'detail' => 'complete cycle (udp/53 dns)',
1025 'err_msg' => 'could not complete SPA cycle',
1026 'function' => \&spa_cycle,
1027 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1028 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1029 "$local_key_file --verbose --verbose",
1030 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1031 "$fwknopdCmd $default_server_conf_args $intf_str",
1032 'fw_rule_created' => $NEW_RULE_REQUIRED,
1033 'fw_rule_removed' => $NEW_RULE_REMOVED,
1037 'category' => 'Rijndael SPA',
1038 'subcategory' => 'client+server',
1039 'detail' => "-P bpf SPA over port $non_std_spa_port",
1040 'err_msg' => 'could not complete SPA cycle',
1041 'function' => \&spa_cycle,
1042 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1043 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1044 "$fwknopdCmd $default_server_conf_args $intf_str " .
1045 qq|-P "udp port $non_std_spa_port"|,
1046 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1047 'fw_rule_created' => $NEW_RULE_REQUIRED,
1048 'fw_rule_removed' => $NEW_RULE_REMOVED,
1053 'category' => 'Rijndael SPA',
1054 'subcategory' => 'client+server',
1055 'detail' => 'random SPA port (tcp/22 ssh)',
1056 'err_msg' => 'could not complete SPA cycle',
1057 'function' => \&spa_cycle,
1058 'cmdline' => "$default_client_args -r",
1059 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1060 "$fwknopdCmd $default_server_conf_args $intf_str " .
1062 'fw_rule_created' => $NEW_RULE_REQUIRED,
1063 'fw_rule_removed' => $NEW_RULE_REMOVED,
1068 'category' => 'Rijndael SPA',
1069 'subcategory' => 'client+server',
1070 'detail' => 'spoof username (tcp/22)',
1071 'err_msg' => 'could not spoof username',
1072 'function' => \&spoof_username,
1073 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1074 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1075 "$local_key_file --verbose --verbose",
1076 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1077 "$fwknopdCmd $default_server_conf_args $intf_str",
1082 'category' => 'Rijndael SPA',
1083 'subcategory' => 'client+server',
1084 'detail' => 'replay attack detection',
1085 'err_msg' => 'could not detect replay attack',
1086 'function' => \&replay_detection,
1087 'cmdline' => $default_client_args,
1088 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1089 "$fwknopdCmd $default_server_conf_args $intf_str",
1093 'category' => 'Rijndael SPA',
1094 'subcategory' => 'server',
1095 'detail' => 'digest cache structure',
1096 'err_msg' => 'improper digest cache structure',
1097 'function' => \&digest_cache_structure,
1102 'category' => 'Rijndael SPA',
1103 'subcategory' => 'client+server',
1104 'detail' => 'non-base64 altered SPA data',
1105 'err_msg' => 'allowed improper SPA data',
1106 'function' => \&altered_non_base64_spa_data,
1107 'cmdline' => $default_client_args,
1108 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1109 "$fwknopdCmd $default_server_conf_args $intf_str",
1113 'category' => 'Rijndael SPA',
1114 'subcategory' => 'client+server',
1115 'detail' => 'base64 altered SPA data',
1116 'err_msg' => 'allowed improper SPA data',
1117 'function' => \&altered_base64_spa_data,
1118 'cmdline' => $default_client_args,
1119 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1120 "$fwknopdCmd $default_server_conf_args $intf_str",
1124 'category' => 'Rijndael SPA',
1125 'subcategory' => 'client+server',
1126 'detail' => 'appended data to SPA pkt',
1127 'err_msg' => 'allowed improper SPA data',
1128 'function' => \&appended_spa_data,
1129 'cmdline' => $default_client_args,
1130 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1131 "$fwknopdCmd $default_server_conf_args $intf_str",
1135 'category' => 'Rijndael SPA',
1136 'subcategory' => 'client+server',
1137 'detail' => 'prepended data to SPA pkt',
1138 'err_msg' => 'allowed improper SPA data',
1139 'function' => \&prepended_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",
1147 'category' => 'GnuPG (GPG) SPA',
1148 'subcategory' => 'client+server',
1149 'detail' => 'complete cycle (tcp/22 ssh)',
1150 'err_msg' => 'could not complete SPA cycle',
1151 'function' => \&spa_cycle,
1152 'cmdline' => $default_client_gpg_args,
1153 'fwknopd_cmdline' => $default_server_gpg_args,
1154 'fw_rule_created' => $NEW_RULE_REQUIRED,
1155 'fw_rule_removed' => $NEW_RULE_REMOVED,
1159 'category' => 'GnuPG (GPG) SPA',
1160 'subcategory' => 'client+server',
1161 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1162 'err_msg' => 'could not complete SPA cycle',
1163 'function' => \&spa_cycle,
1164 'cmdline' => $default_client_gpg_args,
1165 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1166 "$valgrind_str $fwknopdCmd -c $default_conf " .
1167 "-a $multi_gpg_access_conf $intf_str " .
1168 "-d $default_digest_file -p $default_pid_file",
1169 'fw_rule_created' => $NEW_RULE_REQUIRED,
1170 'fw_rule_removed' => $NEW_RULE_REMOVED,
1175 'category' => 'GnuPG (GPG) SPA',
1176 'subcategory' => 'client+server',
1177 'detail' => 'complete cycle (tcp/23 telnet)',
1178 'err_msg' => 'could not complete SPA cycle',
1179 'function' => \&spa_cycle,
1180 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1181 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1182 "$local_key_file --verbose --verbose " .
1183 "--gpg-recipient-key $gpg_server_key " .
1184 "--gpg-signer-key $gpg_client_key " .
1185 "--gpg-home-dir $gpg_client_home_dir",
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' => 'complete cycle (tcp/9418 git)',
1195 'err_msg' => 'could not complete SPA cycle',
1196 'function' => \&spa_cycle,
1197 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1198 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1199 "$local_key_file --verbose --verbose " .
1200 "--gpg-recipient-key $gpg_server_key " .
1201 "--gpg-signer-key $gpg_client_key " .
1202 "--gpg-home-dir $gpg_client_home_dir",
1203 'fwknopd_cmdline' => $default_server_gpg_args,
1204 'fw_rule_created' => $NEW_RULE_REQUIRED,
1205 'fw_rule_removed' => $NEW_RULE_REMOVED,
1209 'category' => 'GnuPG (GPG) SPA',
1210 'subcategory' => 'client+server',
1211 'detail' => 'complete cycle (udp/53 dns)',
1212 'err_msg' => 'could not complete SPA cycle',
1213 'function' => \&spa_cycle,
1214 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1215 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1216 "$local_key_file --verbose --verbose " .
1217 "--gpg-recipient-key $gpg_server_key " .
1218 "--gpg-signer-key $gpg_client_key " .
1219 "--gpg-home-dir $gpg_client_home_dir",
1220 'fwknopd_cmdline' => $default_server_gpg_args,
1221 'fw_rule_created' => $NEW_RULE_REQUIRED,
1222 'fw_rule_removed' => $NEW_RULE_REMOVED,
1227 'category' => 'GnuPG (GPG) SPA',
1228 'subcategory' => 'client+server',
1229 'detail' => 'replay attack detection',
1230 'err_msg' => 'could not detect replay attack',
1231 'function' => \&replay_detection,
1232 'cmdline' => $default_client_gpg_args,
1233 'fwknopd_cmdline' => $default_server_gpg_args,
1238 'category' => 'GnuPG (GPG) SPA',
1239 'subcategory' => 'client+server',
1240 'detail' => 'non-base64 altered SPA data',
1241 'err_msg' => 'allowed improper SPA data',
1242 'function' => \&altered_non_base64_spa_data,
1243 'cmdline' => $default_client_gpg_args,
1244 'fwknopd_cmdline' => $default_server_gpg_args,
1248 'category' => 'GnuPG (GPG) SPA',
1249 'subcategory' => 'client+server',
1250 'detail' => 'base64 altered SPA data',
1251 'err_msg' => 'allowed improper SPA data',
1252 'function' => \&altered_base64_spa_data,
1253 'cmdline' => $default_client_gpg_args,
1254 'fwknopd_cmdline' => $default_server_gpg_args,
1258 'category' => 'GnuPG (GPG) SPA',
1259 'subcategory' => 'client+server',
1260 'detail' => 'appended data to SPA pkt',
1261 'err_msg' => 'allowed improper SPA data',
1262 'function' => \&appended_spa_data,
1263 'cmdline' => $default_client_gpg_args,
1264 'fwknopd_cmdline' => $default_server_gpg_args,
1268 'category' => 'GnuPG (GPG) SPA',
1269 'subcategory' => 'client+server',
1270 'detail' => 'prepended data to SPA pkt',
1271 'err_msg' => 'allowed improper SPA data',
1272 'function' => \&prepended_spa_data,
1273 'cmdline' => $default_client_gpg_args,
1274 'fwknopd_cmdline' => $default_server_gpg_args,
1278 'category' => 'GnuPG (GPG) SPA',
1279 'subcategory' => 'client+server',
1280 'detail' => 'spoof username (tcp/22 ssh)',
1281 'err_msg' => 'could not spoof username',
1282 'function' => \&spoof_username,
1283 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1284 'fwknopd_cmdline' => $default_server_gpg_args,
1288 'category' => 'GnuPG (GPG) SPA',
1289 'subcategory' => 'server',
1290 'detail' => 'digest cache structure',
1291 'err_msg' => 'improper digest cache structure',
1292 'function' => \&digest_cache_structure,
1297 if ($use_valgrind) {
1300 'category' => 'valgrind output',
1301 'subcategory' => 'flagged functions',
1303 'err_msg' => 'could not parse flagged functions',
1304 'function' => \&parse_valgrind_flagged_functions,
1310 'category' => $REQUIRED,
1311 'subcategory' => $OPTIONAL,
1312 'detail' => $REQUIRED,
1313 'function' => $REQUIRED,
1314 'binary' => $OPTIONAL,
1315 'cmdline' => $OPTIONAL,
1316 'fwknopd_cmdline' => $OPTIONAL,
1317 'fatal' => $OPTIONAL,
1318 'exec_err' => $OPTIONAL,
1319 'fw_rule_created' => $OPTIONAL,
1320 'fw_rule_removed' => $OPTIONAL,
1321 'server_conf' => $OPTIONAL,
1322 'positive_output_matches' => $OPTIONAL,
1323 'negative_output_matches' => $OPTIONAL,
1324 'server_positive_output_matches' => $OPTIONAL,
1325 'server_negative_output_matches' => $OPTIONAL,
1329 &diff_test_results();
1333 ### make sure everything looks as expected before continuing
1336 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1337 " args: @args_cp\n\n"
1340 ### save the results from any previous test suite run
1341 ### so that we can potentially compare them with --diff
1342 if ($saved_last_results) {
1343 &logr(" Saved results from previous run " .
1344 "to: ${output_dir}.last/\n\n");
1347 ### main loop through all of the tests
1348 for my $test_hr (@tests) {
1349 &run_test($test_hr);
1352 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1354 copy $logfile, "$output_dir/$logfile" or die $!;
1358 #===================== end main =======================
1361 my $test_hr = shift;
1363 my $msg = "[$test_hr->{'category'}]";
1364 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1365 $msg .= " $test_hr->{'detail'}";
1367 return unless &process_include_exclude($msg);
1377 $current_test_file = "$output_dir/$executed.test";
1378 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1380 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1381 $test_hr->{'msg'} = $msg;
1382 if (&{$test_hr->{'function'}}($test_hr)) {
1383 &logr("pass ($executed)\n");
1386 &logr("fail ($executed)\n");
1389 if ($test_hr->{'fatal'} eq $YES) {
1390 die "[*] required test failed, exiting.";
1397 sub process_include_exclude() {
1400 ### inclusions/exclusions
1401 if (@tests_to_include) {
1403 for my $test (@tests_to_include) {
1404 if ($msg =~ /$test/ or ($use_valgrind
1405 and $msg =~ /valgrind\soutput/)) {
1410 return 0 unless $found;
1412 if (@tests_to_exclude) {
1414 for my $test (@tests_to_exclude) {
1415 if ($msg =~ /$test/) {
1425 sub diff_test_results() {
1427 $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
1428 $diff_dir2 = $output_dir unless $diff_dir2;
1430 die "[*] Need results from a previous run before running --diff"
1431 unless -d $diff_dir2;
1432 die "[*] Current results set does not exist." unless -d $diff_dir1;
1434 my %current_tests = ();
1435 my %previous_tests = ();
1437 ### Only diff results for matching tests (parse the logfile to see which
1438 ### test numbers match across the two test cycles).
1439 &build_results_hash(\%current_tests, $diff_dir1);
1440 &build_results_hash(\%previous_tests, $diff_dir2);
1442 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1443 keys %current_tests) {
1444 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1445 my $current_num = $current_tests{$test_msg}{'num'};
1446 if (defined $previous_tests{$test_msg}) {
1447 print "[+] Checking: $test_msg\n";
1448 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1449 my $previous_num = $previous_tests{$test_msg}{'num'};
1450 if ($current_result ne $previous_result) {
1451 print " DIFF: **$current_result** $test_msg\n";
1454 &diff_results($previous_num, $current_num);
1462 sub diff_results() {
1463 my ($previous_num, $current_num) = @_;
1465 ### edit out any valgrind "==354==" prefixes
1466 my $valgrind_search_re = qr/^==\d+==\s/;
1468 ### remove CMD timestamps
1469 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1471 for my $file ("$diff_dir1/${previous_num}.test",
1472 "$diff_dir1/${previous_num}_fwknopd.test",
1473 "$diff_dir2/${current_num}.test",
1474 "$diff_dir2/${current_num}_fwknopd.test",
1476 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1477 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1480 if (-e "$diff_dir1/${previous_num}.test"
1481 and -e "$diff_dir2/${current_num}.test") {
1482 system "diff -u $diff_dir1/${previous_num}.test " .
1483 "$diff_dir2/${current_num}.test";
1486 if (-e "$diff_dir1/${previous_num}_fwknopd.test"
1487 and -e "$diff_dir2/${current_num}_fwknopd.test") {
1488 system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
1489 "$diff_dir2/${current_num}_fwknopd.test";
1495 sub build_results_hash() {
1496 my ($hr, $dir) = @_;
1498 open F, "< $dir/$logfile" or die $!;
1500 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1501 $hr->{$1}{'pass_fail'} = $2;
1502 $hr->{$1}{'num'} = $3;
1508 sub compile_warnings() {
1510 ### 'make clean' as root
1511 return 0 unless &run_cmd('make -C .. clean',
1512 $cmd_out_tmp, $current_test_file);
1515 my $username = getpwuid((stat($configure_path))[4]);
1516 die "[*] Could not determine $configure_path owner"
1519 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1520 $cmd_out_tmp, $current_test_file);
1524 return 0 unless &run_cmd('make -C ..',
1525 $cmd_out_tmp, $current_test_file);
1529 ### look for compilation warnings - something like:
1530 ### warning: ‘test’ is used uninitialized in this function
1531 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
1532 $current_test_file);
1534 ### the new binaries should exist
1535 unless (-e $fwknopCmd and -x $fwknopCmd) {
1536 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1537 $current_test_file);
1539 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1540 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1541 $current_test_file);
1547 sub binary_exists() {
1548 my $test_hr = shift;
1549 return 0 unless $test_hr->{'binary'};
1551 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1552 ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X)
1554 if ($test_hr->{'binary'} =~ /libfko/) {
1555 unless (-e $test_hr->{'binary'}) {
1556 my $file = "$lib_dir/libfko.dylib";
1558 $test_hr->{'binary'} = $file;
1559 $libfko_bin = $file;
1561 for my $f (glob("$lib_dir/libfko.so*")) {
1562 if (-e $f and -x $f) {
1563 $test_hr->{'binary'} = $f;
1572 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1576 sub expected_code_version() {
1577 my $test_hr = shift;
1579 unless (-e '../VERSION') {
1580 &write_test_file("[-] ../VERSION file does not exist.\n",
1581 $current_test_file);
1585 open F, '< ../VERSION' or die $!;
1588 if ($line =~ /(\d.*\d)/) {
1590 return 0 unless &run_cmd($test_hr->{'cmdline'},
1591 $cmd_out_tmp, $current_test_file);
1592 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1597 sub client_send_spa_packet() {
1598 my $test_hr = shift;
1600 &write_key('fwknoptest', $local_key_file);
1602 return 0 unless &run_cmd($test_hr->{'cmdline'},
1603 $cmd_out_tmp, $current_test_file);
1604 return 0 unless &file_find_regex([qr/final\spacked/i],
1605 $current_test_file);
1611 my $test_hr = shift;
1613 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1614 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1616 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1617 $rv = 0 unless $fw_rule_created;
1618 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1619 $rv = 0 if $fw_rule_created;
1622 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1623 $rv = 0 unless $fw_rule_removed;
1624 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1625 $rv = 0 if $fw_rule_removed;
1628 if ($test_hr->{'server_positive_output_matches'}) {
1629 $rv = 0 unless &file_find_regex(
1630 $test_hr->{'server_positive_output_matches'},
1634 if ($test_hr->{'server_negative_output_matches'}) {
1635 $rv = 0 if &file_find_regex(
1636 $test_hr->{'server_negative_output_matches'},
1643 sub spoof_username() {
1644 my $test_hr = shift;
1646 my $rv = &spa_cycle($test_hr);
1648 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1649 $current_test_file)) {
1653 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1654 $server_test_file)) {
1661 sub replay_detection() {
1662 my $test_hr = shift;
1664 ### do a complete SPA cycle and then parse the SPA packet out of the
1665 ### current test file and re-send
1667 return 0 unless &spa_cycle($test_hr);
1669 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1672 &write_test_file("[-] could not get SPA packet " .
1673 "from file: $current_test_file\n",
1674 $current_test_file);
1681 'port' => $default_spa_port,
1682 'dst_ip' => $loopback_ip,
1687 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1688 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1690 $rv = 0 unless $server_was_stopped;
1692 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1693 $server_test_file)) {
1700 sub digest_cache_structure() {
1701 my $test_hr = shift;
1704 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1706 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1708 ### the format should be:
1709 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1710 open F, "< $default_digest_file" or
1711 die "[*] could not open $default_digest_file: $!";
1715 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1716 &write_test_file("[-] invalid digest.cache line: $_",
1717 $current_test_file);
1723 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1724 &write_test_file("[+] DBM digest file format, " .
1725 "assuming this is valid.\n", $current_test_file);
1727 ### don't know what kind of file the digest.cache is
1728 &write_test_file("[-] unrecognized file type for " .
1729 "$default_digest_file.\n", $current_test_file);
1734 &write_test_file("[+] valid digest.cache structure.\n",
1735 $current_test_file);
1741 sub server_bpf_ignore_packet() {
1742 my $test_hr = shift;
1745 my $server_was_stopped = 0;
1746 my $fw_rule_created = 0;
1747 my $fw_rule_removed = 0;
1749 unless (&client_send_spa_packet($test_hr)) {
1750 &write_test_file("[-] fwknop client execution error.\n",
1751 $current_test_file);
1755 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1758 &write_test_file("[-] could not get SPA packet " .
1759 "from file: $current_test_file\n", $current_test_file);
1766 'port' => $default_spa_port,
1767 'dst_ip' => $loopback_ip,
1772 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1773 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1775 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1776 $server_test_file)) {
1783 sub altered_non_base64_spa_data() {
1784 my $test_hr = shift;
1787 my $server_was_stopped = 0;
1788 my $fw_rule_created = 0;
1789 my $fw_rule_removed = 0;
1791 unless (&client_send_spa_packet($test_hr)) {
1792 &write_test_file("[-] fwknop client execution error.\n",
1793 $current_test_file);
1797 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1800 &write_test_file("[-] could not get SPA packet " .
1801 "from file: $current_test_file\n", $current_test_file);
1805 ### alter one byte (change to a ":")
1806 $spa_pkt =~ s|^(.{3}).|$1:|;
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 $rv = 0 unless $server_was_stopped;
1825 sub altered_base64_spa_data() {
1826 my $test_hr = shift;
1829 my $server_was_stopped = 0;
1830 my $fw_rule_created = 0;
1831 my $fw_rule_removed = 0;
1833 unless (&client_send_spa_packet($test_hr)) {
1834 &write_test_file("[-] fwknop client execution error.\n",
1835 $current_test_file);
1839 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1842 &write_test_file("[-] could not get SPA packet " .
1843 "from file: $current_test_file\n", $current_test_file);
1847 $spa_pkt =~ s|^(.{3}).|AAAA|;
1852 'port' => $default_spa_port,
1853 'dst_ip' => $loopback_ip,
1858 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1859 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1861 $rv = 0 unless $server_was_stopped;
1863 if ($fw_rule_created) {
1864 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1867 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1870 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1871 $server_test_file)) {
1878 sub appended_spa_data() {
1879 my $test_hr = shift;
1882 my $server_was_stopped = 0;
1883 my $fw_rule_created = 0;
1884 my $fw_rule_removed = 0;
1886 unless (&client_send_spa_packet($test_hr)) {
1887 &write_test_file("[-] fwknop client execution error.\n",
1888 $current_test_file);
1892 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1895 &write_test_file("[-] could not get SPA packet " .
1896 "from file: $current_test_file\n", $current_test_file);
1905 'port' => $default_spa_port,
1906 'dst_ip' => $loopback_ip,
1911 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1912 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1914 $rv = 0 unless $server_was_stopped;
1916 if ($fw_rule_created) {
1917 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1920 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1923 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1924 $server_test_file)) {
1931 sub prepended_spa_data() {
1932 my $test_hr = shift;
1935 my $server_was_stopped = 0;
1936 my $fw_rule_created = 0;
1937 my $fw_rule_removed = 0;
1939 unless (&client_send_spa_packet($test_hr)) {
1940 &write_test_file("[-] fwknop client execution error.\n",
1941 $current_test_file);
1945 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1948 &write_test_file("[-] could not get SPA packet " .
1949 "from file: $current_test_file\n", $current_test_file);
1953 $spa_pkt = 'AAAA' . $spa_pkt;
1958 'port' => $default_spa_port,
1959 'dst_ip' => $loopback_ip,
1964 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1965 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1967 $rv = 0 unless $server_was_stopped;
1969 if ($fw_rule_created) {
1970 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1973 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1976 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1977 $server_test_file)) {
1984 sub server_start() {
1985 my $test_hr = shift;
1987 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1988 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1990 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1991 $server_test_file)) {
1995 $rv = 0 unless $server_was_stopped;
2001 my $test_hr = shift;
2003 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2004 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
2006 $rv = 0 unless $server_was_stopped;
2011 sub server_packet_limit() {
2012 my $test_hr = shift;
2017 'port' => $default_spa_port,
2018 'dst_ip' => $loopback_ip,
2023 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2024 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2026 if (&is_fwknopd_running()) {
2031 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2032 $server_test_file)) {
2036 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2037 $server_test_file)) {
2044 sub server_ignore_small_packets() {
2045 my $test_hr = shift;
2050 'port' => $default_spa_port,
2051 'dst_ip' => $loopback_ip,
2052 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2056 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2057 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2061 if (&is_fwknopd_running()) {
2069 sub client_server_interaction() {
2070 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2073 my $server_was_stopped = 1;
2074 my $fw_rule_created = 1;
2075 my $fw_rule_removed = 0;
2077 ### start fwknopd to monitor for the SPA packet over the loopback interface
2078 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2080 ### give fwknopd a chance to parse its config and start sniffing
2081 ### on the loopback interface
2082 if ($use_valgrind) {
2088 ### send the SPA packet(s) to the server either manually using IO::Socket or
2089 ### with the fwknopd client
2090 if ($spa_client_flag == $USE_CLIENT) {
2091 unless (&client_send_spa_packet($test_hr)) {
2092 &write_test_file("[-] fwknop client execution error.\n",
2093 $current_test_file);
2097 &send_packets($pkts_hr);
2100 ### check to see if the SPA packet resulted in a new fw access rule
2102 while (not &is_fw_rule_active($test_hr)) {
2103 &write_test_file("[-] new fw rule does not exist.\n",
2104 $current_test_file);
2110 $fw_rule_created = 0;
2111 $fw_rule_removed = 0;
2114 &time_for_valgrind() if $use_valgrind;
2116 if ($fw_rule_created) {
2117 sleep 3; ### allow time for rule time out.
2118 if (&is_fw_rule_active($test_hr)) {
2119 &write_test_file("[-] new fw rule not timed out.\n",
2120 $current_test_file);
2123 &write_test_file("[+] new fw rule timed out.\n",
2124 $current_test_file);
2125 $fw_rule_removed = 1;
2129 if (&is_fwknopd_running()) {
2131 unless (&file_find_regex([qr/Got\sSIGTERM/],
2132 $server_test_file)) {
2133 $server_was_stopped = 0;
2136 &write_test_file("[-] server is not running.\n",
2137 $current_test_file);
2138 $server_was_stopped = 0;
2141 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2144 sub get_spa_packet_from_file() {
2149 my $found_trigger_line = 0;
2150 open F, "< $file" or die "[*] Could not open file $file: $!";
2152 if (/final\spacked/i) {
2153 $found_trigger_line = 1;
2156 next unless $found_trigger_line;
2158 ### the next line with non whitespace is the SPA packet
2169 sub send_packets() {
2170 my $pkts_ar = shift;
2172 open F, ">> $current_test_file" or die $!;
2173 print F "[+] send_packets(): Sending the following packets...\n";
2174 print F Dumper $pkts_ar;
2177 for my $pkt_hr (@$pkts_ar) {
2178 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2179 my $socket = IO::Socket::INET->new(
2180 PeerAddr => $pkt_hr->{'dst_ip'},
2181 PeerPort => $pkt_hr->{'port'},
2182 Proto => $pkt_hr->{'proto'},
2184 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2185 "socket to $pkt_hr->{'dst_ip'}: $!";
2187 $socket->send($pkt_hr->{'data'});
2190 } elsif ($pkt_hr->{'proto'} eq 'http') {
2192 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2196 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2201 sub generic_exec() {
2202 my $test_hr = shift;
2206 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2207 $cmd_out_tmp, $current_test_file);
2209 if ($test_hr->{'exec_err'} eq $YES) {
2210 $rv = 0 if $exec_rv;
2212 $rv = 0 unless $exec_rv;
2215 if ($test_hr->{'positive_output_matches'}) {
2216 $rv = 0 unless &file_find_regex(
2217 $test_hr->{'positive_output_matches'},
2218 $current_test_file);
2221 if ($test_hr->{'negative_output_matches'}) {
2222 $rv = 0 if &file_find_regex(
2223 $test_hr->{'negative_output_matches'},
2224 $current_test_file);
2232 my $test_hr = shift;
2233 return 0 unless $test_hr->{'binary'};
2234 &run_cmd("./hardening-check $test_hr->{'binary'}",
2235 $cmd_out_tmp, $current_test_file);
2236 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2237 $current_test_file);
2241 ### check for stack protection
2242 sub stack_protected_binary() {
2243 my $test_hr = shift;
2244 return 0 unless $test_hr->{'binary'};
2245 &run_cmd("./hardening-check $test_hr->{'binary'}",
2246 $cmd_out_tmp, $current_test_file);
2247 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2248 $current_test_file);
2252 ### check for fortified source functions
2253 sub fortify_source_functions() {
2254 my $test_hr = shift;
2255 return 0 unless $test_hr->{'binary'};
2256 &run_cmd("./hardening-check $test_hr->{'binary'}",
2257 $cmd_out_tmp, $current_test_file);
2258 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2259 $current_test_file);
2263 ### check for read-only relocations
2264 sub read_only_relocations() {
2265 my $test_hr = shift;
2266 return 0 unless $test_hr->{'binary'};
2267 &run_cmd("./hardening-check $test_hr->{'binary'}",
2268 $cmd_out_tmp, $current_test_file);
2269 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2270 $current_test_file);
2274 ### check for immediate binding
2275 sub immediate_binding() {
2276 my $test_hr = shift;
2277 return 0 unless $test_hr->{'binary'};
2278 &run_cmd("./hardening-check $test_hr->{'binary'}",
2279 $cmd_out_tmp, $current_test_file);
2280 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2281 $current_test_file);
2287 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2288 "$default_server_conf_args --fw-list-all",
2289 $cmd_out_tmp, $current_test_file);
2297 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2298 'if [ `which iptables` ]; then iptables -V; fi',
2299 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2300 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2301 'if [ `which gpg` ]; then gpg --version; fi',
2302 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2306 'ls -l /usr/lib/*pcap*',
2307 'ls -l /usr/local/lib/*pcap*',
2308 'ls -l /usr/lib/*fko*',
2309 'ls -l /usr/local/lib/*fko*',
2311 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2313 if ($cmd =~ /^ldd/) {
2314 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2318 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2319 ### to enable gpg tests
2320 unless ($have_gpgme == 3) {
2321 push @tests_to_exclude, "GPG";
2327 sub time_for_valgrind() {
2329 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2330 "grep valgrind |grep -v perl | grep -v grep",
2331 $cmd_out_tmp, $current_test_file)) {
2339 sub anonymize_results() {
2341 die "[*] $output_dir does not exist" unless -d $output_dir;
2342 die "[*] $logfile does not exist, has $0 been executed?"
2345 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2348 ### remove non-loopback IP addresses
2349 my $search_re = qr/\b127\.0\.0\.1\b/;
2350 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2351 $search_re = qr/\b127\.0\.0\.2\b/;
2352 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2353 $search_re = qr/\b0\.0\.0\.0\b/;
2354 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2355 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2356 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2357 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2358 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2359 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2361 ### remove hostname from any uname output
2362 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2363 system "perl -p -i -e 'undef \$/; s|$search_re" .
2364 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2366 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2367 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2370 system "tar cvfz $tarfile $logfile $output_dir";
2371 print "[+] Anonymized test results file: $tarfile\n";
2380 my $test_hr = shift;
2382 open F, "> $default_pid_file" or die $!;
2386 &server_start($test_hr);
2388 open F, "< $default_pid_file" or die $!;
2400 sub start_fwknopd() {
2401 my $test_hr = shift;
2403 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2406 die "[*] Could not fork: $!" unless defined $pid;
2410 ### we are the child, so start fwknopd
2411 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2412 $server_cmd_tmp, $server_test_file);
2418 my ($key, $file) = @_;
2420 open K, "> $file" or die "[*] Could not open $file: $!";
2421 print K "$loopback_ip: $key\n";
2422 print K "localhost: $key\n";
2423 print K "some.host.through.proxy.com: $key\n";
2429 open C, ">> $current_test_file"
2430 or die "[*] Could not open $current_test_file: $!";
2431 print C "\n" . localtime() . " [+] PID dump:\n";
2433 &run_cmd("ps auxww | grep knop |grep -v grep",
2434 $cmd_out_tmp, $current_test_file);
2439 my ($cmd, $cmd_out, $file) = @_;
2443 or die "[*] Could not open $file: $!";
2444 print F localtime() . " CMD: $cmd\n";
2448 or die "[*] Could not open $file: $!";
2449 print F localtime() . " CMD: $cmd\n";
2453 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2455 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2456 my @cmd_lines = <C>;
2459 open F, ">> $file" or die "[*] Could not open $file: $!";
2460 print F $_ for @cmd_lines;
2473 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2482 $|++; ### turn off buffering
2484 $< == 0 && $> == 0 or
2485 die "[*] $0: You must be root (or equivalent ",
2486 "UID 0 account) to effectively test fwknop";
2488 ### validate test hashes
2490 for my $test_hr (@tests) {
2491 for my $key (keys %test_keys) {
2492 if ($test_keys{$key} == $REQUIRED) {
2493 die "[*] Missing '$key' element in hash: $hash_num"
2494 unless defined $test_hr->{$key};
2496 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2502 if ($use_valgrind) {
2503 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2504 unless -e $valgrindCmd and -x $valgrindCmd;
2507 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2508 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2510 for my $file ($configure_path,
2513 $default_access_conf,
2514 $no_source_match_access_conf,
2515 $ip_source_match_access_conf,
2516 $subnet_source_match_access_conf,
2517 $no_subnet_source_match_access_conf,
2518 $no_multi_source_match_access_conf,
2519 $multi_source_match_access_conf,
2520 $open_ports_access_conf,
2521 $mismatch_open_ports_access_conf,
2522 $require_user_access_conf,
2523 $mismatch_user_access_conf,
2524 $require_src_access_conf,
2525 $multi_gpg_access_conf,
2526 $multi_stanzas_access_conf,
2527 $expired_access_conf,
2528 $expired_epoch_access_conf,
2529 $future_expired_access_conf,
2530 $invalid_expire_access_conf,
2531 $force_nat_access_conf,
2533 die "[*] $file does not exist" unless -e $file;
2536 if (-d $output_dir) {
2537 if (-d "${output_dir}.last") {
2538 rmtree "${output_dir}.last"
2539 or die "[*] rmtree ${output_dir}.last $!";
2541 mkdir "${output_dir}.last"
2542 or die "[*] ${output_dir}.last: $!";
2543 for my $file (glob("$output_dir/*.test")) {
2544 if ($file =~ m|.*/(.*)|) {
2545 copy $file, "${output_dir}.last/$1" or die $!;
2548 if (-e "$output_dir/init") {
2549 copy "$output_dir/init", "${output_dir}.last/init";
2552 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2554 $saved_last_results = 1;
2556 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2558 unless (-d $run_dir) {
2559 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2562 for my $file (glob("$output_dir/*.test")) {
2563 unlink $file or die "[*] Could not unlink($file)";
2565 if (-e "$output_dir/init") {
2566 unlink "$output_dir/init" or die $!;
2570 unlink $logfile or die $!;
2573 if ($test_include) {
2574 @tests_to_include = split /\s*,\s*/, $test_include;
2576 if ($test_exclude) {
2577 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2580 ### make sure no fwknopd instance is currently running
2581 die "[*] Please stop the running fwknopd instance."
2582 if &is_fwknopd_running();
2584 unless ($enable_recompilation_warnings_check) {
2585 push @tests_to_exclude, 'recompilation';
2588 $sudo_path = &find_command('sudo');
2590 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2591 ### disable compilation checks
2592 push @tests_to_exclude, 'recompilation';
2595 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2598 $platform = 'linux';
2604 unless ($platform eq 'linux') {
2605 push @tests_to_exclude, 'NAT';
2611 sub identify_loopback_intf() {
2612 return if $loopback_intf;
2616 ### lo Link encap:Local Loopback
2617 ### inet addr:127.0.0.1 Mask:255.0.0.0
2618 ### inet6 addr: ::1/128 Scope:Host
2619 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2620 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2621 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2622 ### collisions:0 txqueuelen:0
2623 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2627 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2628 ### options=3<RXCSUM,TXCSUM>
2629 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2630 ### inet6 ::1 prefixlen 128
2631 ### inet 127.0.0.1 netmask 0xff000000
2632 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2635 my $found_loopback_intf = 0;
2637 my $cmd = 'ifconfig -a';
2638 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2640 if (/^(\S+?):?\s+.*loopback/i) {
2644 if (/^\S/ and $intf and not $found_loopback_intf) {
2645 ### should not happen
2648 if ($intf and /\b127\.0\.0\.1\b/) {
2649 $found_loopback_intf = 1;
2655 die "[*] could not determine loopback interface, use --loopback <name>"
2656 unless $found_loopback_intf;
2658 $loopback_intf = $intf;
2663 sub parse_valgrind_flagged_functions() {
2664 for my $file (glob("$output_dir/*.test")) {
2665 my $type = 'server';
2666 $type = 'client' if $file =~ /\d\.test/;
2667 open F, "< $file" or die $!;
2669 ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
2670 if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
2671 $valgrind_flagged_fcns{$type}{"$1 $2"}++;
2672 $valgrind_flagged_fcns_unique{$type}{$1}++;
2678 open F, ">> $current_test_file" or die $!;
2679 for my $type ('client', 'server') {
2680 print F "\n[+] fwknop $type functions (unique view):\n";
2681 next unless defined $valgrind_flagged_fcns_unique{$type};
2682 for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
2683 <=> $valgrind_flagged_fcns_unique{$type}{$a}}
2684 keys %{$valgrind_flagged_fcns_unique{$type}}) {
2685 printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
2687 print F "\n[+] fwknop $type functions (with call line numbers):\n";
2688 for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
2689 <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
2690 printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
2692 next unless defined $valgrind_flagged_fcns{$type};
2699 sub is_fw_rule_active() {
2700 my $test_hr = shift;
2702 my $conf_args = $default_server_conf_args;
2704 if ($test_hr->{'server_conf'}) {
2705 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2706 "-d $default_digest_file -p $default_pid_file";
2709 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2710 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2711 $cmd_out_tmp, $current_test_file);
2715 sub is_fwknopd_running() {
2717 sleep 2 if $use_valgrind;
2719 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2720 "--status", $cmd_out_tmp, $current_test_file);
2722 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2727 sub stop_fwknopd() {
2729 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2730 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2732 if ($use_valgrind) {
2733 &time_for_valgrind();
2741 sub file_find_regex() {
2742 my ($re_ar, $file) = @_;
2744 my $found_all_regexs = 1;
2745 my @write_lines = ();
2746 my @file_lines = ();
2748 open F, "< $file" or die "[*] Could not open $file: $!";
2750 push @file_lines, $_;
2754 for my $re (@$re_ar) {
2756 for my $line (@file_lines) {
2758 push @write_lines, "[+] file_find_regex() " .
2759 "Matched '$re' with line: $line";
2764 push @write_lines, "[-] file_find_regex() " .
2765 "Did not match any regex in '@$re_ar' in file: $file\n";
2766 $found_all_regexs = 0;
2770 for my $line (@write_lines) {
2771 &write_test_file($line, $file);
2774 return $found_all_regexs;
2777 sub find_command() {
2781 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2783 if (m|^(/.*$cmd)$|) {
2792 sub write_test_file() {
2793 my ($msg, $file) = @_;
2797 or die "[*] Could not open $file: $!";
2802 or die "[*] Could not open $file: $!";
2812 open F, ">> $logfile" or die $!;
2823 -A --Anonymize-results - Prepare anonymized results at:
2825 --diff - Compare the results of one test run to
2826 another. By default this compares output
2827 in ${output_dir}.last to $output_dir
2828 --diff-dir1=<path> - Left hand side of diff directory path,
2829 default is: ${output_dir}.last
2830 --diff-dir2=<path> - Right hand side of diff directory path,
2831 default is: $output_dir
2832 --include=<regex> - Specify a regex to be used over test
2833 names that must match.
2834 --exclude=<regex> - Specify a regex to be used over test
2835 names that must not match.
2836 --enable-recompile - Recompile fwknop sources and look for
2837 compilation warnings.
2838 --enable-valgrind - Run every test underneath valgrind.
2839 --List - List test names.
2840 --loopback-intf=<intf> - Specify loopback interface name (default
2841 depends on the OS where the test suite
2843 --output-dir=<path> - Path to output directory, default is:
2845 --fwknop-path=<path> - Path to fwknop binary, default is:
2847 --fwknopd-path=<path> - Path to fwknopd binary, default is:
2849 --libfko-path=<path> - Path to libfko, default is:
2851 --valgrind-path=<path> - Path to valgrind, default is:
2853 -h --help - Display usage on STDOUT and exit.