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' => 'complete cycle (tcp/23 telnet)',
974 'err_msg' => 'could not complete SPA cycle',
975 'function' => \&spa_cycle,
976 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
977 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
978 "$local_key_file --verbose --verbose",
979 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
980 "$fwknopdCmd $default_server_conf_args $intf_str",
981 'fw_rule_created' => $NEW_RULE_REQUIRED,
982 'fw_rule_removed' => $NEW_RULE_REMOVED,
986 'category' => 'Rijndael SPA',
987 'subcategory' => 'client+server',
988 'detail' => 'complete cycle (tcp/9418 git)',
989 'err_msg' => 'could not complete SPA cycle',
990 'function' => \&spa_cycle,
991 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
992 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
993 "$local_key_file --verbose --verbose",
994 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
995 "$fwknopdCmd $default_server_conf_args $intf_str",
996 'fw_rule_created' => $NEW_RULE_REQUIRED,
997 'fw_rule_removed' => $NEW_RULE_REMOVED,
1001 'category' => 'Rijndael SPA',
1002 'subcategory' => 'client+server',
1003 'detail' => 'complete cycle (udp/53 dns)',
1004 'err_msg' => 'could not complete SPA cycle',
1005 'function' => \&spa_cycle,
1006 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1007 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1008 "$local_key_file --verbose --verbose",
1009 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1010 "$fwknopdCmd $default_server_conf_args $intf_str",
1011 'fw_rule_created' => $NEW_RULE_REQUIRED,
1012 'fw_rule_removed' => $NEW_RULE_REMOVED,
1016 'category' => 'Rijndael SPA',
1017 'subcategory' => 'client+server',
1018 'detail' => "-P bpf SPA over port $non_std_spa_port",
1019 'err_msg' => 'could not complete SPA cycle',
1020 'function' => \&spa_cycle,
1021 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1022 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1023 "$fwknopdCmd $default_server_conf_args $intf_str " .
1024 qq|-P "udp port $non_std_spa_port"|,
1025 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1026 'fw_rule_created' => $NEW_RULE_REQUIRED,
1027 'fw_rule_removed' => $NEW_RULE_REMOVED,
1032 'category' => 'Rijndael SPA',
1033 'subcategory' => 'client+server',
1034 'detail' => 'random SPA port (tcp/22 ssh)',
1035 'err_msg' => 'could not complete SPA cycle',
1036 'function' => \&spa_cycle,
1037 'cmdline' => "$default_client_args -r",
1038 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1039 "$fwknopdCmd $default_server_conf_args $intf_str " .
1041 'fw_rule_created' => $NEW_RULE_REQUIRED,
1042 'fw_rule_removed' => $NEW_RULE_REMOVED,
1047 'category' => 'Rijndael SPA',
1048 'subcategory' => 'client+server',
1049 'detail' => 'spoof username (tcp/22)',
1050 'err_msg' => 'could not spoof username',
1051 'function' => \&spoof_username,
1052 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1053 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1054 "$local_key_file --verbose --verbose",
1055 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1056 "$fwknopdCmd $default_server_conf_args $intf_str",
1061 'category' => 'Rijndael SPA',
1062 'subcategory' => 'client+server',
1063 'detail' => 'replay attack detection',
1064 'err_msg' => 'could not detect replay attack',
1065 'function' => \&replay_detection,
1066 'cmdline' => $default_client_args,
1067 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1068 "$fwknopdCmd $default_server_conf_args $intf_str",
1072 'category' => 'Rijndael SPA',
1073 'subcategory' => 'server',
1074 'detail' => 'digest cache structure',
1075 'err_msg' => 'improper digest cache structure',
1076 'function' => \&digest_cache_structure,
1081 'category' => 'Rijndael SPA',
1082 'subcategory' => 'client+server',
1083 'detail' => 'non-base64 altered SPA data',
1084 'err_msg' => 'allowed improper SPA data',
1085 'function' => \&altered_non_base64_spa_data,
1086 'cmdline' => $default_client_args,
1087 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1088 "$fwknopdCmd $default_server_conf_args $intf_str",
1092 'category' => 'Rijndael SPA',
1093 'subcategory' => 'client+server',
1094 'detail' => 'base64 altered SPA data',
1095 'err_msg' => 'allowed improper SPA data',
1096 'function' => \&altered_base64_spa_data,
1097 'cmdline' => $default_client_args,
1098 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1099 "$fwknopdCmd $default_server_conf_args $intf_str",
1103 'category' => 'Rijndael SPA',
1104 'subcategory' => 'client+server',
1105 'detail' => 'appended data to SPA pkt',
1106 'err_msg' => 'allowed improper SPA data',
1107 'function' => \&appended_spa_data,
1108 'cmdline' => $default_client_args,
1109 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1110 "$fwknopdCmd $default_server_conf_args $intf_str",
1114 'category' => 'Rijndael SPA',
1115 'subcategory' => 'client+server',
1116 'detail' => 'prepended data to SPA pkt',
1117 'err_msg' => 'allowed improper SPA data',
1118 'function' => \&prepended_spa_data,
1119 'cmdline' => $default_client_args,
1120 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1121 "$fwknopdCmd $default_server_conf_args $intf_str",
1126 'category' => 'GnuPG (GPG) SPA',
1127 'subcategory' => 'client+server',
1128 'detail' => 'complete cycle (tcp/22 ssh)',
1129 'err_msg' => 'could not complete SPA cycle',
1130 'function' => \&spa_cycle,
1131 'cmdline' => $default_client_gpg_args,
1132 'fwknopd_cmdline' => $default_server_gpg_args,
1133 'fw_rule_created' => $NEW_RULE_REQUIRED,
1134 'fw_rule_removed' => $NEW_RULE_REMOVED,
1138 'category' => 'GnuPG (GPG) SPA',
1139 'subcategory' => 'client+server',
1140 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1141 'err_msg' => 'could not complete SPA cycle',
1142 'function' => \&spa_cycle,
1143 'cmdline' => $default_client_gpg_args,
1144 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1145 "$valgrind_str $fwknopdCmd -c $default_conf " .
1146 "-a $multi_gpg_access_conf $intf_str " .
1147 "-d $default_digest_file -p $default_pid_file",
1148 'fw_rule_created' => $NEW_RULE_REQUIRED,
1149 'fw_rule_removed' => $NEW_RULE_REMOVED,
1154 'category' => 'GnuPG (GPG) SPA',
1155 'subcategory' => 'client+server',
1156 'detail' => 'complete cycle (tcp/23 telnet)',
1157 'err_msg' => 'could not complete SPA cycle',
1158 'function' => \&spa_cycle,
1159 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1160 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1161 "$local_key_file --verbose --verbose " .
1162 "--gpg-recipient-key $gpg_server_key " .
1163 "--gpg-signer-key $gpg_client_key " .
1164 "--gpg-home-dir $gpg_client_home_dir",
1165 'fwknopd_cmdline' => $default_server_gpg_args,
1166 'fw_rule_created' => $NEW_RULE_REQUIRED,
1167 'fw_rule_removed' => $NEW_RULE_REMOVED,
1171 'category' => 'GnuPG (GPG) SPA',
1172 'subcategory' => 'client+server',
1173 'detail' => 'complete cycle (tcp/9418 git)',
1174 'err_msg' => 'could not complete SPA cycle',
1175 'function' => \&spa_cycle,
1176 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1177 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1178 "$local_key_file --verbose --verbose " .
1179 "--gpg-recipient-key $gpg_server_key " .
1180 "--gpg-signer-key $gpg_client_key " .
1181 "--gpg-home-dir $gpg_client_home_dir",
1182 'fwknopd_cmdline' => $default_server_gpg_args,
1183 'fw_rule_created' => $NEW_RULE_REQUIRED,
1184 'fw_rule_removed' => $NEW_RULE_REMOVED,
1188 'category' => 'GnuPG (GPG) SPA',
1189 'subcategory' => 'client+server',
1190 'detail' => 'complete cycle (udp/53 dns)',
1191 'err_msg' => 'could not complete SPA cycle',
1192 'function' => \&spa_cycle,
1193 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1194 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1195 "$local_key_file --verbose --verbose " .
1196 "--gpg-recipient-key $gpg_server_key " .
1197 "--gpg-signer-key $gpg_client_key " .
1198 "--gpg-home-dir $gpg_client_home_dir",
1199 'fwknopd_cmdline' => $default_server_gpg_args,
1200 'fw_rule_created' => $NEW_RULE_REQUIRED,
1201 'fw_rule_removed' => $NEW_RULE_REMOVED,
1206 'category' => 'GnuPG (GPG) SPA',
1207 'subcategory' => 'client+server',
1208 'detail' => 'replay attack detection',
1209 'err_msg' => 'could not detect replay attack',
1210 'function' => \&replay_detection,
1211 'cmdline' => $default_client_gpg_args,
1212 'fwknopd_cmdline' => $default_server_gpg_args,
1217 'category' => 'GnuPG (GPG) SPA',
1218 'subcategory' => 'client+server',
1219 'detail' => 'non-base64 altered SPA data',
1220 'err_msg' => 'allowed improper SPA data',
1221 'function' => \&altered_non_base64_spa_data,
1222 'cmdline' => $default_client_gpg_args,
1223 'fwknopd_cmdline' => $default_server_gpg_args,
1227 'category' => 'GnuPG (GPG) SPA',
1228 'subcategory' => 'client+server',
1229 'detail' => 'base64 altered SPA data',
1230 'err_msg' => 'allowed improper SPA data',
1231 'function' => \&altered_base64_spa_data,
1232 'cmdline' => $default_client_gpg_args,
1233 'fwknopd_cmdline' => $default_server_gpg_args,
1237 'category' => 'GnuPG (GPG) SPA',
1238 'subcategory' => 'client+server',
1239 'detail' => 'appended data to SPA pkt',
1240 'err_msg' => 'allowed improper SPA data',
1241 'function' => \&appended_spa_data,
1242 'cmdline' => $default_client_gpg_args,
1243 'fwknopd_cmdline' => $default_server_gpg_args,
1247 'category' => 'GnuPG (GPG) SPA',
1248 'subcategory' => 'client+server',
1249 'detail' => 'prepended data to SPA pkt',
1250 'err_msg' => 'allowed improper SPA data',
1251 'function' => \&prepended_spa_data,
1252 'cmdline' => $default_client_gpg_args,
1253 'fwknopd_cmdline' => $default_server_gpg_args,
1257 'category' => 'GnuPG (GPG) SPA',
1258 'subcategory' => 'client+server',
1259 'detail' => 'spoof username (tcp/22 ssh)',
1260 'err_msg' => 'could not spoof username',
1261 'function' => \&spoof_username,
1262 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1263 'fwknopd_cmdline' => $default_server_gpg_args,
1267 'category' => 'GnuPG (GPG) SPA',
1268 'subcategory' => 'server',
1269 'detail' => 'digest cache structure',
1270 'err_msg' => 'improper digest cache structure',
1271 'function' => \&digest_cache_structure,
1276 if ($use_valgrind) {
1279 'category' => 'valgrind output',
1280 'subcategory' => 'flagged functions',
1282 'err_msg' => 'could not parse flagged functions',
1283 'function' => \&parse_valgrind_flagged_functions,
1289 'category' => $REQUIRED,
1290 'subcategory' => $OPTIONAL,
1291 'detail' => $REQUIRED,
1292 'function' => $REQUIRED,
1293 'binary' => $OPTIONAL,
1294 'cmdline' => $OPTIONAL,
1295 'fwknopd_cmdline' => $OPTIONAL,
1296 'fatal' => $OPTIONAL,
1297 'exec_err' => $OPTIONAL,
1298 'fw_rule_created' => $OPTIONAL,
1299 'fw_rule_removed' => $OPTIONAL,
1300 'server_conf' => $OPTIONAL,
1301 'positive_output_matches' => $OPTIONAL,
1302 'negative_output_matches' => $OPTIONAL,
1303 'server_positive_output_matches' => $OPTIONAL,
1304 'server_negative_output_matches' => $OPTIONAL,
1308 &diff_test_results();
1312 ### make sure everything looks as expected before continuing
1315 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1316 " args: @args_cp\n\n"
1319 ### save the results from any previous test suite run
1320 ### so that we can potentially compare them with --diff
1321 if ($saved_last_results) {
1322 &logr(" Saved results from previous run " .
1323 "to: ${output_dir}.last/\n\n");
1326 ### main loop through all of the tests
1327 for my $test_hr (@tests) {
1328 &run_test($test_hr);
1331 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1333 copy $logfile, "$output_dir/$logfile" or die $!;
1337 #===================== end main =======================
1340 my $test_hr = shift;
1342 my $msg = "[$test_hr->{'category'}]";
1343 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1344 $msg .= " $test_hr->{'detail'}";
1346 return unless &process_include_exclude($msg);
1356 $current_test_file = "$output_dir/$executed.test";
1357 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1359 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1360 $test_hr->{'msg'} = $msg;
1361 if (&{$test_hr->{'function'}}($test_hr)) {
1362 &logr("pass ($executed)\n");
1365 &logr("fail ($executed)\n");
1368 if ($test_hr->{'fatal'} eq $YES) {
1369 die "[*] required test failed, exiting.";
1376 sub process_include_exclude() {
1379 ### inclusions/exclusions
1380 if (@tests_to_include) {
1382 for my $test (@tests_to_include) {
1383 if ($msg =~ /$test/ or ($use_valgrind
1384 and $msg =~ /valgrind\soutput/)) {
1389 return 0 unless $found;
1391 if (@tests_to_exclude) {
1393 for my $test (@tests_to_exclude) {
1394 if ($msg =~ /$test/) {
1404 sub diff_test_results() {
1406 $diff_dir1 = "${output_dir}.last" unless $diff_dir1;
1407 $diff_dir2 = $output_dir unless $diff_dir2;
1409 die "[*] Need results from a previous run before running --diff"
1410 unless -d $diff_dir2;
1411 die "[*] Current results set does not exist." unless -d $diff_dir1;
1413 my %current_tests = ();
1414 my %previous_tests = ();
1416 ### Only diff results for matching tests (parse the logfile to see which
1417 ### test numbers match across the two test cycles).
1418 &build_results_hash(\%current_tests, $diff_dir1);
1419 &build_results_hash(\%previous_tests, $diff_dir2);
1421 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1422 keys %current_tests) {
1423 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1424 my $current_num = $current_tests{$test_msg}{'num'};
1425 if (defined $previous_tests{$test_msg}) {
1426 print "[+] Checking: $test_msg\n";
1427 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1428 my $previous_num = $previous_tests{$test_msg}{'num'};
1429 if ($current_result ne $previous_result) {
1430 print " DIFF: **$current_result** $test_msg\n";
1433 &diff_results($previous_num, $current_num);
1441 sub diff_results() {
1442 my ($previous_num, $current_num) = @_;
1444 ### edit out any valgrind "==354==" prefixes
1445 my $valgrind_search_re = qr/^==\d+==\s/;
1447 ### remove CMD timestamps
1448 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1450 for my $file ("$diff_dir1/${previous_num}.test",
1451 "$diff_dir1/${previous_num}_fwknopd.test",
1452 "$diff_dir2/${current_num}.test",
1453 "$diff_dir2/${current_num}_fwknopd.test",
1455 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1456 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1459 if (-e "$diff_dir1/${previous_num}.test"
1460 and -e "$diff_dir2/${current_num}.test") {
1461 system "diff -u $diff_dir1/${previous_num}.test " .
1462 "$diff_dir2/${current_num}.test";
1465 if (-e "$diff_dir1/${previous_num}_fwknopd.test"
1466 and -e "$diff_dir2/${current_num}_fwknopd.test") {
1467 system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
1468 "$diff_dir2/${current_num}_fwknopd.test";
1474 sub build_results_hash() {
1475 my ($hr, $dir) = @_;
1477 open F, "< $dir/$logfile" or die $!;
1479 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1480 $hr->{$1}{'pass_fail'} = $2;
1481 $hr->{$1}{'num'} = $3;
1487 sub compile_warnings() {
1489 ### 'make clean' as root
1490 return 0 unless &run_cmd('make -C .. clean',
1491 $cmd_out_tmp, $current_test_file);
1494 my $username = getpwuid((stat($configure_path))[4]);
1495 die "[*] Could not determine $configure_path owner"
1498 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1499 $cmd_out_tmp, $current_test_file);
1503 return 0 unless &run_cmd('make -C ..',
1504 $cmd_out_tmp, $current_test_file);
1508 ### look for compilation warnings - something like:
1509 ### warning: ‘test’ is used uninitialized in this function
1510 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/],
1511 $current_test_file);
1513 ### the new binaries should exist
1514 unless (-e $fwknopCmd and -x $fwknopCmd) {
1515 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1516 $current_test_file);
1518 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1519 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1520 $current_test_file);
1526 sub binary_exists() {
1527 my $test_hr = shift;
1528 return 0 unless $test_hr->{'binary'};
1530 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1531 ### libfko.so link on OpenBSD, and libfko.dylib path on Mac OS X)
1533 if ($test_hr->{'binary'} =~ /libfko/) {
1534 unless (-e $test_hr->{'binary'}) {
1535 my $file = "$lib_dir/libfko.dylib";
1537 $test_hr->{'binary'} = $file;
1538 $libfko_bin = $file;
1540 for my $f (glob("$lib_dir/libfko.so*")) {
1541 if (-e $f and -x $f) {
1542 $test_hr->{'binary'} = $f;
1551 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1555 sub expected_code_version() {
1556 my $test_hr = shift;
1558 unless (-e '../VERSION') {
1559 &write_test_file("[-] ../VERSION file does not exist.\n",
1560 $current_test_file);
1564 open F, '< ../VERSION' or die $!;
1567 if ($line =~ /(\d.*\d)/) {
1569 return 0 unless &run_cmd($test_hr->{'cmdline'},
1570 $cmd_out_tmp, $current_test_file);
1571 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1576 sub client_send_spa_packet() {
1577 my $test_hr = shift;
1579 &write_key('fwknoptest', $local_key_file);
1581 return 0 unless &run_cmd($test_hr->{'cmdline'},
1582 $cmd_out_tmp, $current_test_file);
1583 return 0 unless &file_find_regex([qr/final\spacked/i],
1584 $current_test_file);
1590 my $test_hr = shift;
1592 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1593 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1595 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1596 $rv = 0 unless $fw_rule_created;
1597 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1598 $rv = 0 if $fw_rule_created;
1601 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1602 $rv = 0 unless $fw_rule_removed;
1603 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1604 $rv = 0 if $fw_rule_removed;
1607 if ($test_hr->{'server_positive_output_matches'}) {
1608 $rv = 0 unless &file_find_regex(
1609 $test_hr->{'server_positive_output_matches'},
1613 if ($test_hr->{'server_negative_output_matches'}) {
1614 $rv = 0 if &file_find_regex(
1615 $test_hr->{'server_negative_output_matches'},
1622 sub spoof_username() {
1623 my $test_hr = shift;
1625 my $rv = &spa_cycle($test_hr);
1627 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1628 $current_test_file)) {
1632 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1633 $server_test_file)) {
1640 sub replay_detection() {
1641 my $test_hr = shift;
1643 ### do a complete SPA cycle and then parse the SPA packet out of the
1644 ### current test file and re-send
1646 return 0 unless &spa_cycle($test_hr);
1648 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1651 &write_test_file("[-] could not get SPA packet " .
1652 "from file: $current_test_file\n",
1653 $current_test_file);
1660 'port' => $default_spa_port,
1661 'dst_ip' => $loopback_ip,
1666 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1667 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1669 $rv = 0 unless $server_was_stopped;
1671 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1672 $server_test_file)) {
1679 sub digest_cache_structure() {
1680 my $test_hr = shift;
1683 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1685 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1687 ### the format should be:
1688 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1689 open F, "< $default_digest_file" or
1690 die "[*] could not open $default_digest_file: $!";
1694 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1695 &write_test_file("[-] invalid digest.cache line: $_",
1696 $current_test_file);
1702 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1703 &write_test_file("[+] DBM digest file format, " .
1704 "assuming this is valid.\n", $current_test_file);
1706 ### don't know what kind of file the digest.cache is
1707 &write_test_file("[-] unrecognized file type for " .
1708 "$default_digest_file.\n", $current_test_file);
1713 &write_test_file("[+] valid digest.cache structure.\n",
1714 $current_test_file);
1720 sub server_bpf_ignore_packet() {
1721 my $test_hr = shift;
1724 my $server_was_stopped = 0;
1725 my $fw_rule_created = 0;
1726 my $fw_rule_removed = 0;
1728 unless (&client_send_spa_packet($test_hr)) {
1729 &write_test_file("[-] fwknop client execution error.\n",
1730 $current_test_file);
1734 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1737 &write_test_file("[-] could not get SPA packet " .
1738 "from file: $current_test_file\n", $current_test_file);
1745 'port' => $default_spa_port,
1746 'dst_ip' => $loopback_ip,
1751 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1752 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1754 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1755 $server_test_file)) {
1762 sub altered_non_base64_spa_data() {
1763 my $test_hr = shift;
1766 my $server_was_stopped = 0;
1767 my $fw_rule_created = 0;
1768 my $fw_rule_removed = 0;
1770 unless (&client_send_spa_packet($test_hr)) {
1771 &write_test_file("[-] fwknop client execution error.\n",
1772 $current_test_file);
1776 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1779 &write_test_file("[-] could not get SPA packet " .
1780 "from file: $current_test_file\n", $current_test_file);
1784 ### alter one byte (change to a ":")
1785 $spa_pkt =~ s|^(.{3}).|$1:|;
1790 'port' => $default_spa_port,
1791 'dst_ip' => $loopback_ip,
1796 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1797 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1799 $rv = 0 unless $server_was_stopped;
1804 sub altered_base64_spa_data() {
1805 my $test_hr = shift;
1808 my $server_was_stopped = 0;
1809 my $fw_rule_created = 0;
1810 my $fw_rule_removed = 0;
1812 unless (&client_send_spa_packet($test_hr)) {
1813 &write_test_file("[-] fwknop client execution error.\n",
1814 $current_test_file);
1818 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1821 &write_test_file("[-] could not get SPA packet " .
1822 "from file: $current_test_file\n", $current_test_file);
1826 $spa_pkt =~ s|^(.{3}).|AAAA|;
1831 'port' => $default_spa_port,
1832 'dst_ip' => $loopback_ip,
1837 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1838 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1840 $rv = 0 unless $server_was_stopped;
1842 if ($fw_rule_created) {
1843 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1846 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1849 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1850 $server_test_file)) {
1857 sub appended_spa_data() {
1858 my $test_hr = shift;
1861 my $server_was_stopped = 0;
1862 my $fw_rule_created = 0;
1863 my $fw_rule_removed = 0;
1865 unless (&client_send_spa_packet($test_hr)) {
1866 &write_test_file("[-] fwknop client execution error.\n",
1867 $current_test_file);
1871 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1874 &write_test_file("[-] could not get SPA packet " .
1875 "from file: $current_test_file\n", $current_test_file);
1884 'port' => $default_spa_port,
1885 'dst_ip' => $loopback_ip,
1890 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1891 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1893 $rv = 0 unless $server_was_stopped;
1895 if ($fw_rule_created) {
1896 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1899 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1902 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1903 $server_test_file)) {
1910 sub prepended_spa_data() {
1911 my $test_hr = shift;
1914 my $server_was_stopped = 0;
1915 my $fw_rule_created = 0;
1916 my $fw_rule_removed = 0;
1918 unless (&client_send_spa_packet($test_hr)) {
1919 &write_test_file("[-] fwknop client execution error.\n",
1920 $current_test_file);
1924 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1927 &write_test_file("[-] could not get SPA packet " .
1928 "from file: $current_test_file\n", $current_test_file);
1932 $spa_pkt = 'AAAA' . $spa_pkt;
1937 'port' => $default_spa_port,
1938 'dst_ip' => $loopback_ip,
1943 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1944 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1946 $rv = 0 unless $server_was_stopped;
1948 if ($fw_rule_created) {
1949 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1952 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1955 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1956 $server_test_file)) {
1963 sub server_start() {
1964 my $test_hr = shift;
1966 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1967 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1969 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1970 $server_test_file)) {
1974 $rv = 0 unless $server_was_stopped;
1980 my $test_hr = shift;
1982 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1983 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1985 $rv = 0 unless $server_was_stopped;
1990 sub server_packet_limit() {
1991 my $test_hr = shift;
1996 'port' => $default_spa_port,
1997 'dst_ip' => $loopback_ip,
2002 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2003 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2005 if (&is_fwknopd_running()) {
2010 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2011 $server_test_file)) {
2015 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2016 $server_test_file)) {
2023 sub server_ignore_small_packets() {
2024 my $test_hr = shift;
2029 'port' => $default_spa_port,
2030 'dst_ip' => $loopback_ip,
2031 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2035 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2036 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2040 if (&is_fwknopd_running()) {
2048 sub client_server_interaction() {
2049 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2052 my $server_was_stopped = 1;
2053 my $fw_rule_created = 1;
2054 my $fw_rule_removed = 0;
2056 ### start fwknopd to monitor for the SPA packet over the loopback interface
2057 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2059 ### give fwknopd a chance to parse its config and start sniffing
2060 ### on the loopback interface
2061 if ($use_valgrind) {
2067 ### send the SPA packet(s) to the server either manually using IO::Socket or
2068 ### with the fwknopd client
2069 if ($spa_client_flag == $USE_CLIENT) {
2070 unless (&client_send_spa_packet($test_hr)) {
2071 &write_test_file("[-] fwknop client execution error.\n",
2072 $current_test_file);
2076 &send_packets($pkts_hr);
2079 ### check to see if the SPA packet resulted in a new fw access rule
2081 while (not &is_fw_rule_active($test_hr)) {
2082 &write_test_file("[-] new fw rule does not exist.\n",
2083 $current_test_file);
2089 $fw_rule_created = 0;
2090 $fw_rule_removed = 0;
2093 &time_for_valgrind() if $use_valgrind;
2095 if ($fw_rule_created) {
2096 sleep 3; ### allow time for rule time out.
2097 if (&is_fw_rule_active($test_hr)) {
2098 &write_test_file("[-] new fw rule not timed out.\n",
2099 $current_test_file);
2102 &write_test_file("[+] new fw rule timed out.\n",
2103 $current_test_file);
2104 $fw_rule_removed = 1;
2108 if (&is_fwknopd_running()) {
2110 unless (&file_find_regex([qr/Got\sSIGTERM/],
2111 $server_test_file)) {
2112 $server_was_stopped = 0;
2115 &write_test_file("[-] server is not running.\n",
2116 $current_test_file);
2117 $server_was_stopped = 0;
2120 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2123 sub get_spa_packet_from_file() {
2128 my $found_trigger_line = 0;
2129 open F, "< $file" or die "[*] Could not open file $file: $!";
2131 if (/final\spacked/i) {
2132 $found_trigger_line = 1;
2135 next unless $found_trigger_line;
2137 ### the next line with non whitespace is the SPA packet
2148 sub send_packets() {
2149 my $pkts_ar = shift;
2151 open F, ">> $current_test_file" or die $!;
2152 print F "[+] send_packets(): Sending the following packets...\n";
2153 print F Dumper $pkts_ar;
2156 for my $pkt_hr (@$pkts_ar) {
2157 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2158 my $socket = IO::Socket::INET->new(
2159 PeerAddr => $pkt_hr->{'dst_ip'},
2160 PeerPort => $pkt_hr->{'port'},
2161 Proto => $pkt_hr->{'proto'},
2163 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2164 "socket to $pkt_hr->{'dst_ip'}: $!";
2166 $socket->send($pkt_hr->{'data'});
2169 } elsif ($pkt_hr->{'proto'} eq 'http') {
2171 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2175 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2180 sub generic_exec() {
2181 my $test_hr = shift;
2185 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2186 $cmd_out_tmp, $current_test_file);
2188 if ($test_hr->{'exec_err'} eq $YES) {
2189 $rv = 0 if $exec_rv;
2191 $rv = 0 unless $exec_rv;
2194 if ($test_hr->{'positive_output_matches'}) {
2195 $rv = 0 unless &file_find_regex(
2196 $test_hr->{'positive_output_matches'},
2197 $current_test_file);
2200 if ($test_hr->{'negative_output_matches'}) {
2201 $rv = 0 if &file_find_regex(
2202 $test_hr->{'negative_output_matches'},
2203 $current_test_file);
2211 my $test_hr = shift;
2212 return 0 unless $test_hr->{'binary'};
2213 &run_cmd("./hardening-check $test_hr->{'binary'}",
2214 $cmd_out_tmp, $current_test_file);
2215 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2216 $current_test_file);
2220 ### check for stack protection
2221 sub stack_protected_binary() {
2222 my $test_hr = shift;
2223 return 0 unless $test_hr->{'binary'};
2224 &run_cmd("./hardening-check $test_hr->{'binary'}",
2225 $cmd_out_tmp, $current_test_file);
2226 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2227 $current_test_file);
2231 ### check for fortified source functions
2232 sub fortify_source_functions() {
2233 my $test_hr = shift;
2234 return 0 unless $test_hr->{'binary'};
2235 &run_cmd("./hardening-check $test_hr->{'binary'}",
2236 $cmd_out_tmp, $current_test_file);
2237 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2238 $current_test_file);
2242 ### check for read-only relocations
2243 sub read_only_relocations() {
2244 my $test_hr = shift;
2245 return 0 unless $test_hr->{'binary'};
2246 &run_cmd("./hardening-check $test_hr->{'binary'}",
2247 $cmd_out_tmp, $current_test_file);
2248 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2249 $current_test_file);
2253 ### check for immediate binding
2254 sub immediate_binding() {
2255 my $test_hr = shift;
2256 return 0 unless $test_hr->{'binary'};
2257 &run_cmd("./hardening-check $test_hr->{'binary'}",
2258 $cmd_out_tmp, $current_test_file);
2259 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2260 $current_test_file);
2266 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2267 "$default_server_conf_args --fw-list-all",
2268 $cmd_out_tmp, $current_test_file);
2276 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2277 'if [ `which iptables` ]; then iptables -V; fi',
2278 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2279 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2280 'if [ `which gpg` ]; then gpg --version; fi',
2281 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2285 'ls -l /usr/lib/*pcap*',
2286 'ls -l /usr/local/lib/*pcap*',
2287 'ls -l /usr/lib/*fko*',
2288 'ls -l /usr/local/lib/*fko*',
2290 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2292 if ($cmd =~ /^ldd/) {
2293 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2297 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2298 ### to enable gpg tests
2299 unless ($have_gpgme == 3) {
2300 push @tests_to_exclude, "GPG";
2306 sub time_for_valgrind() {
2308 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2309 "grep valgrind |grep -v perl | grep -v grep",
2310 $cmd_out_tmp, $current_test_file)) {
2318 sub anonymize_results() {
2320 die "[*] $output_dir does not exist" unless -d $output_dir;
2321 die "[*] $logfile does not exist, has $0 been executed?"
2324 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2327 ### remove non-loopback IP addresses
2328 my $search_re = qr/\b127\.0\.0\.1\b/;
2329 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2330 $search_re = qr/\b127\.0\.0\.2\b/;
2331 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2332 $search_re = qr/\b0\.0\.0\.0\b/;
2333 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2334 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2335 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2336 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2337 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2338 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2340 ### remove hostname from any uname output
2341 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2342 system "perl -p -i -e 'undef \$/; s|$search_re" .
2343 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2345 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2346 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2349 system "tar cvfz $tarfile $logfile $output_dir";
2350 print "[+] Anonymized test results file: $tarfile\n";
2359 my $test_hr = shift;
2361 open F, "> $default_pid_file" or die $!;
2365 &server_start($test_hr);
2367 open F, "< $default_pid_file" or die $!;
2379 sub start_fwknopd() {
2380 my $test_hr = shift;
2382 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2385 die "[*] Could not fork: $!" unless defined $pid;
2389 ### we are the child, so start fwknopd
2390 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2391 $server_cmd_tmp, $server_test_file);
2397 my ($key, $file) = @_;
2399 open K, "> $file" or die "[*] Could not open $file: $!";
2400 print K "$loopback_ip: $key\n";
2401 print K "localhost: $key\n";
2402 print K "some.host.through.proxy.com: $key\n";
2408 open C, ">> $current_test_file"
2409 or die "[*] Could not open $current_test_file: $!";
2410 print C "\n" . localtime() . " [+] PID dump:\n";
2412 &run_cmd("ps auxww | grep knop |grep -v grep",
2413 $cmd_out_tmp, $current_test_file);
2418 my ($cmd, $cmd_out, $file) = @_;
2422 or die "[*] Could not open $file: $!";
2423 print F localtime() . " CMD: $cmd\n";
2427 or die "[*] Could not open $file: $!";
2428 print F localtime() . " CMD: $cmd\n";
2432 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2434 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2435 my @cmd_lines = <C>;
2438 open F, ">> $file" or die "[*] Could not open $file: $!";
2439 print F $_ for @cmd_lines;
2452 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2461 $|++; ### turn off buffering
2463 $< == 0 && $> == 0 or
2464 die "[*] $0: You must be root (or equivalent ",
2465 "UID 0 account) to effectively test fwknop";
2467 ### validate test hashes
2469 for my $test_hr (@tests) {
2470 for my $key (keys %test_keys) {
2471 if ($test_keys{$key} == $REQUIRED) {
2472 die "[*] Missing '$key' element in hash: $hash_num"
2473 unless defined $test_hr->{$key};
2475 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2481 if ($use_valgrind) {
2482 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2483 unless -e $valgrindCmd and -x $valgrindCmd;
2486 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2487 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2489 for my $file ($configure_path,
2492 $default_access_conf,
2493 $no_source_match_access_conf,
2494 $ip_source_match_access_conf,
2495 $subnet_source_match_access_conf,
2496 $no_subnet_source_match_access_conf,
2497 $no_multi_source_match_access_conf,
2498 $multi_source_match_access_conf,
2499 $open_ports_access_conf,
2500 $mismatch_open_ports_access_conf,
2501 $require_user_access_conf,
2502 $mismatch_user_access_conf,
2503 $require_src_access_conf,
2504 $multi_gpg_access_conf,
2505 $multi_stanzas_access_conf,
2506 $expired_access_conf,
2507 $expired_epoch_access_conf,
2508 $future_expired_access_conf,
2509 $invalid_expire_access_conf,
2510 $force_nat_access_conf,
2512 die "[*] $file does not exist" unless -e $file;
2515 if (-d $output_dir) {
2516 if (-d "${output_dir}.last") {
2517 rmtree "${output_dir}.last"
2518 or die "[*] rmtree ${output_dir}.last $!";
2520 mkdir "${output_dir}.last"
2521 or die "[*] ${output_dir}.last: $!";
2522 for my $file (glob("$output_dir/*.test")) {
2523 if ($file =~ m|.*/(.*)|) {
2524 copy $file, "${output_dir}.last/$1" or die $!;
2527 if (-e "$output_dir/init") {
2528 copy "$output_dir/init", "${output_dir}.last/init";
2531 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2533 $saved_last_results = 1;
2535 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2537 unless (-d $run_dir) {
2538 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2541 for my $file (glob("$output_dir/*.test")) {
2542 unlink $file or die "[*] Could not unlink($file)";
2544 if (-e "$output_dir/init") {
2545 unlink "$output_dir/init" or die $!;
2549 unlink $logfile or die $!;
2552 if ($test_include) {
2553 @tests_to_include = split /\s*,\s*/, $test_include;
2555 if ($test_exclude) {
2556 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2559 ### make sure no fwknopd instance is currently running
2560 die "[*] Please stop the running fwknopd instance."
2561 if &is_fwknopd_running();
2563 unless ($enable_recompilation_warnings_check) {
2564 push @tests_to_exclude, 'recompilation';
2567 $sudo_path = &find_command('sudo');
2569 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2570 ### disable compilation checks
2571 push @tests_to_exclude, 'recompilation';
2574 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2577 $platform = 'linux';
2583 unless ($platform eq 'linux') {
2584 push @tests_to_exclude, 'NAT';
2590 sub identify_loopback_intf() {
2591 return if $loopback_intf;
2595 ### lo Link encap:Local Loopback
2596 ### inet addr:127.0.0.1 Mask:255.0.0.0
2597 ### inet6 addr: ::1/128 Scope:Host
2598 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2599 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2600 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2601 ### collisions:0 txqueuelen:0
2602 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2606 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2607 ### options=3<RXCSUM,TXCSUM>
2608 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2609 ### inet6 ::1 prefixlen 128
2610 ### inet 127.0.0.1 netmask 0xff000000
2611 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2614 my $found_loopback_intf = 0;
2616 my $cmd = 'ifconfig -a';
2617 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2619 if (/^(\S+?):?\s+.*loopback/i) {
2623 if (/^\S/ and $intf and not $found_loopback_intf) {
2624 ### should not happen
2627 if ($intf and /\b127\.0\.0\.1\b/) {
2628 $found_loopback_intf = 1;
2634 die "[*] could not determine loopback interface, use --loopback <name>"
2635 unless $found_loopback_intf;
2637 $loopback_intf = $intf;
2642 sub parse_valgrind_flagged_functions() {
2643 for my $file (glob("$output_dir/*.test")) {
2644 my $type = 'server';
2645 $type = 'client' if $file =~ /\d\.test/;
2646 open F, "< $file" or die $!;
2648 ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
2649 if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
2650 $valgrind_flagged_fcns{$type}{"$1 $2"}++;
2651 $valgrind_flagged_fcns_unique{$type}{$1}++;
2657 open F, ">> $current_test_file" or die $!;
2658 for my $type ('client', 'server') {
2659 print F "\n[+] fwknop $type functions (unique view):\n";
2660 next unless defined $valgrind_flagged_fcns_unique{$type};
2661 for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
2662 <=> $valgrind_flagged_fcns_unique{$type}{$a}}
2663 keys %{$valgrind_flagged_fcns_unique{$type}}) {
2664 printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
2666 print F "\n[+] fwknop $type functions (with call line numbers):\n";
2667 for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
2668 <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
2669 printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
2671 next unless defined $valgrind_flagged_fcns{$type};
2678 sub is_fw_rule_active() {
2679 my $test_hr = shift;
2681 my $conf_args = $default_server_conf_args;
2683 if ($test_hr->{'server_conf'}) {
2684 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2685 "-d $default_digest_file -p $default_pid_file";
2688 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2689 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2690 $cmd_out_tmp, $current_test_file);
2694 sub is_fwknopd_running() {
2696 sleep 2 if $use_valgrind;
2698 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2699 "--status", $cmd_out_tmp, $current_test_file);
2701 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2706 sub stop_fwknopd() {
2708 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2709 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2711 if ($use_valgrind) {
2712 &time_for_valgrind();
2720 sub file_find_regex() {
2721 my ($re_ar, $file) = @_;
2723 my $found_all_regexs = 1;
2724 my @write_lines = ();
2725 my @file_lines = ();
2727 open F, "< $file" or die "[*] Could not open $file: $!";
2729 push @file_lines, $_;
2733 for my $re (@$re_ar) {
2735 for my $line (@file_lines) {
2737 push @write_lines, "[+] file_find_regex() " .
2738 "Matched '$re' with line: $line";
2743 push @write_lines, "[-] file_find_regex() " .
2744 "Did not match any regex in '@$re_ar' in file: $file\n";
2745 $found_all_regexs = 0;
2749 for my $line (@write_lines) {
2750 &write_test_file($line, $file);
2753 return $found_all_regexs;
2756 sub find_command() {
2760 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2762 if (m|^(/.*$cmd)$|) {
2771 sub write_test_file() {
2772 my ($msg, $file) = @_;
2776 or die "[*] Could not open $file: $!";
2781 or die "[*] Could not open $file: $!";
2791 open F, ">> $logfile" or die $!;
2802 -A --Anonymize-results - Prepare anonymized results at:
2804 --diff - Compare the results of one test run to
2805 another. By default this compares output
2806 in ${output_dir}.last to $output_dir
2807 --diff-dir1=<path> - Left hand side of diff directory path,
2808 default is: ${output_dir}.last
2809 --diff-dir2=<path> - Right hand side of diff directory path,
2810 default is: $output_dir
2811 --include=<regex> - Specify a regex to be used over test
2812 names that must match.
2813 --exclude=<regex> - Specify a regex to be used over test
2814 names that must not match.
2815 --enable-recompile - Recompile fwknop sources and look for
2816 compilation warnings.
2817 --enable-valgrind - Run every test underneath valgrind.
2818 --List - List test names.
2819 --loopback-intf=<intf> - Specify loopback interface name (default
2820 depends on the OS where the test suite
2822 --output-dir=<path> - Path to output directory, default is:
2824 --fwknop-path=<path> - Path to fwknop binary, default is:
2826 --fwknopd-path=<path> - Path to fwknopd binary, default is:
2828 --libfko-path=<path> - Path to libfko, default is:
2830 --valgrind-path=<path> - Path to valgrind, default is:
2832 -h --help - Display usage on STDOUT and exit.