7 use Getopt::Long 'GetOptions';
10 #==================== config =====================
11 my $logfile = 'test.log';
12 my $local_key_file = 'local_spa.key';
13 my $output_dir = 'output';
14 my $lib_dir = '../lib/.libs';
15 my $conf_dir = 'conf';
17 my $configure_path = '../configure';
18 my $cmd_out_tmp = 'cmd.out';
19 my $server_cmd_tmp = 'server_cmd.out';
20 my $gpg_client_home_dir = "$conf_dir/client-gpg";
22 my $nat_conf = "$conf_dir/nat_fwknopd.conf";
23 my $default_conf = "$conf_dir/default_fwknopd.conf";
24 my $default_access_conf = "$conf_dir/default_access.conf";
25 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
26 my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
27 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
28 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
29 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
30 my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
31 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
32 my $default_digest_file = "$run_dir/digest.cache";
33 my $default_pid_file = "$run_dir/fwknopd.pid";
34 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
35 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
36 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
37 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
38 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
39 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
40 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
41 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
42 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
43 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
44 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
45 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
46 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
47 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
49 my $fwknopCmd = '../client/.libs/fwknop';
50 my $fwknopdCmd = '../server/.libs/fwknopd';
51 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
52 my $valgrindCmd = '/usr/bin/valgrind';
54 my $gpg_server_key = '361BBAD4';
55 my $gpg_client_key = '6A3FAD56';
57 my $loopback_ip = '127.0.0.1';
58 my $fake_ip = '127.0.0.2';
59 my $internal_nat_host = '192.168.1.2';
60 my $force_nat_host = '192.168.1.123';
61 my $default_spa_port = 62201;
62 my $non_std_spa_port = 12345;
64 my $spoof_user = 'testuser';
65 #================== end config ===================
70 my $test_include = '';
71 my @tests_to_include = ();
72 my $test_exclude = '';
73 my @tests_to_exclude = ();
75 my $loopback_intf = '';
76 my $anonymize_results = 0;
77 my $current_test_file = "$output_dir/init";
78 my $tarfile = 'test_fwknop.tar.gz';
79 my $server_test_file = '';
81 my $valgrind_str = '';
82 my $saved_last_results = 0;
84 my $enable_recompilation_warnings_check = 0;
91 my $USE_PREDEF_PKTS = 1;
95 my $NEW_RULE_REQUIRED = 1;
96 my $REQUIRE_NO_NEW_RULE = 2;
97 my $NEW_RULE_REMOVED = 1;
98 my $REQUIRE_NO_NEW_REMOVED = 2;
100 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
104 exit 1 unless GetOptions(
105 'Anonymize-results' => \$anonymize_results,
106 'fwknop-path=s' => \$fwknopCmd,
107 'fwknopd-path=s' => \$fwknopdCmd,
108 'libfko-path=s' => \$libfko_bin,
109 'loopback-intf=s' => \$loopback_intf,
110 'test-include=s' => \$test_include,
111 'include=s' => \$test_include, ### synonym
112 'test-exclude=s' => \$test_exclude,
113 'exclude=s' => \$test_exclude, ### synonym
114 'enable-recompile-check' => \$enable_recompilation_warnings_check,
115 'List-mode' => \$list_mode,
116 'enable-valgrind' => \$use_valgrind,
117 'valgrind-path=s' => \$valgrindCmd,
118 'diff' => \$diff_mode,
124 ### create an anonymized tar file of test suite results that can be
125 ### emailed around to assist in debugging fwknop communications
126 exit &anonymize_results() if $anonymize_results;
128 &identify_loopback_intf();
130 $valgrind_str = "$valgrindCmd --leak-check=full " .
131 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
133 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
135 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
136 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
137 "$local_key_file --verbose --verbose";
139 my $default_client_gpg_args = "$default_client_args " .
140 "--gpg-recipient-key $gpg_server_key " .
141 "--gpg-signer-key $gpg_client_key " .
142 "--gpg-home-dir $gpg_client_home_dir";
144 my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
145 "-d $default_digest_file -p $default_pid_file";
147 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
148 "$valgrind_str $fwknopdCmd -c $default_conf " .
149 "-a $gpg_access_conf $intf_str " .
150 "-d $default_digest_file -p $default_pid_file";
152 ### point the compiled binaries at the local libary path
153 ### instead of any installed libfko instance
154 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
156 ### main array that defines the tests we will run
159 'category' => 'recompilation',
160 'detail' => 'recompile and look for compilation warnings',
161 'err_msg' => 'compile warnings exist',
162 'function' => \&compile_warnings,
166 'category' => 'build',
167 'subcategory' => 'client',
168 'detail' => 'binary exists',
169 'err_msg' => 'binary not found',
170 'function' => \&binary_exists,
171 'binary' => $fwknopCmd,
175 'category' => 'build security',
176 'subcategory' => 'client',
177 'detail' => 'Position Independent Executable (PIE)',
178 'err_msg' => 'non PIE binary (fwknop client)',
179 'function' => \&pie_binary,
180 'binary' => $fwknopCmd,
184 'category' => 'build security',
185 'subcategory' => 'client',
186 'detail' => 'stack protected binary',
187 'err_msg' => 'non stack protected binary (fwknop client)',
188 'function' => \&stack_protected_binary,
189 'binary' => $fwknopCmd,
193 'category' => 'build security',
194 'subcategory' => 'client',
195 'detail' => 'fortify source functions',
196 'err_msg' => 'source functions not fortified (fwknop client)',
197 'function' => \&fortify_source_functions,
198 'binary' => $fwknopCmd,
202 'category' => 'build security',
203 'subcategory' => 'client',
204 'detail' => 'read-only relocations',
205 'err_msg' => 'no read-only relocations (fwknop client)',
206 'function' => \&read_only_relocations,
207 'binary' => $fwknopCmd,
211 'category' => 'build security',
212 'subcategory' => 'client',
213 'detail' => 'immediate binding',
214 'err_msg' => 'no immediate binding (fwknop client)',
215 'function' => \&immediate_binding,
216 'binary' => $fwknopCmd,
221 'category' => 'build',
222 'subcategory' => 'server',
223 'detail' => 'binary exists',
224 'err_msg' => 'binary not found',
225 'function' => \&binary_exists,
226 'binary' => $fwknopdCmd,
231 'category' => 'build security',
232 'subcategory' => 'server',
233 'detail' => 'Position Independent Executable (PIE)',
234 'err_msg' => 'non PIE binary (fwknopd server)',
235 'function' => \&pie_binary,
236 'binary' => $fwknopdCmd,
240 'category' => 'build security',
241 'subcategory' => 'server',
242 'detail' => 'stack protected binary',
243 'err_msg' => 'non stack protected binary (fwknopd server)',
244 'function' => \&stack_protected_binary,
245 'binary' => $fwknopdCmd,
249 'category' => 'build security',
250 'subcategory' => 'server',
251 'detail' => 'fortify source functions',
252 'err_msg' => 'source functions not fortified (fwknopd server)',
253 'function' => \&fortify_source_functions,
254 'binary' => $fwknopdCmd,
258 'category' => 'build security',
259 'subcategory' => 'server',
260 'detail' => 'read-only relocations',
261 'err_msg' => 'no read-only relocations (fwknopd server)',
262 'function' => \&read_only_relocations,
263 'binary' => $fwknopdCmd,
267 'category' => 'build security',
268 'subcategory' => 'server',
269 'detail' => 'immediate binding',
270 'err_msg' => 'no immediate binding (fwknopd server)',
271 'function' => \&immediate_binding,
272 'binary' => $fwknopdCmd,
277 'category' => 'build',
278 'subcategory' => 'libfko',
279 'detail' => 'binary exists',
280 'err_msg' => 'binary not found',
281 'function' => \&binary_exists,
282 'binary' => $libfko_bin,
286 'category' => 'build security',
287 'subcategory' => 'libfko',
288 'detail' => 'stack protected binary',
289 'err_msg' => 'non stack protected binary (libfko)',
290 'function' => \&stack_protected_binary,
291 'binary' => $libfko_bin,
295 'category' => 'build security',
296 'subcategory' => 'libfko',
297 'detail' => 'fortify source functions',
298 'err_msg' => 'source functions not fortified (libfko)',
299 'function' => \&fortify_source_functions,
300 'binary' => $libfko_bin,
304 'category' => 'build security',
305 'subcategory' => 'libfko',
306 'detail' => 'read-only relocations',
307 'err_msg' => 'no read-only relocations (libfko)',
308 'function' => \&read_only_relocations,
309 'binary' => $libfko_bin,
313 'category' => 'build security',
314 'subcategory' => 'libfko',
315 'detail' => 'immediate binding',
316 'err_msg' => 'no immediate binding (libfko)',
317 'function' => \&immediate_binding,
318 'binary' => $libfko_bin,
323 'category' => 'preliminaries',
324 'subcategory' => 'client',
325 'detail' => 'usage info',
326 'err_msg' => 'could not get usage info',
327 'function' => \&generic_exec,
328 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
332 'category' => 'preliminaries',
333 'subcategory' => 'client',
334 'detail' => 'getopt() no such argument',
335 'err_msg' => 'getopt() allowed non-existant argument',
336 'function' => \&generic_exec,
337 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
342 'category' => 'preliminaries',
343 'subcategory' => 'client',
344 'detail' => '--test mode, packet not sent',
345 'err_msg' => '--test mode, packet sent?',
346 'function' => \&generic_exec,
347 'positive_output_matches' => [qr/test\smode\senabled/],
348 'cmdline' => "$default_client_args --test",
353 'category' => 'preliminaries',
354 'subcategory' => 'client',
355 'detail' => 'expected code version',
356 'err_msg' => 'code version mis-match',
357 'function' => \&expected_code_version,
358 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
363 'category' => 'preliminaries',
364 'subcategory' => 'server',
365 'detail' => 'usage info',
366 'err_msg' => 'could not get usage info',
367 'function' => \&generic_exec,
368 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
372 'category' => 'preliminaries',
373 'subcategory' => 'server',
374 'detail' => 'getopt() no such argument',
375 'err_msg' => 'getopt() allowed non-existant argument',
376 'function' => \&generic_exec,
377 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
383 'category' => 'preliminaries',
384 'subcategory' => 'server',
385 'detail' => 'expected code version',
386 'err_msg' => 'code version mis-match',
387 'function' => \&expected_code_version,
388 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
389 "$fwknopdCmd -c $default_conf -a " .
390 "$default_access_conf --version",
394 'category' => 'preliminaries',
395 'detail' => 'collecting system specifics',
396 'err_msg' => 'could not get complete system specs',
397 'function' => \&specs,
398 'binary' => $fwknopdCmd,
403 'category' => 'basic operations',
404 'detail' => 'dump config',
405 'err_msg' => 'could not dump configuration',
406 'function' => \&generic_exec,
407 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
409 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
410 "$fwknopdCmd -c $default_conf " .
411 "-a $default_access_conf --dump-config",
415 'category' => 'basic operations',
416 'detail' => 'override config',
417 'err_msg' => 'could not override configuration',
418 'function' => \&generic_exec,
419 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
421 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
422 "$fwknopdCmd $default_server_conf_args " .
423 "-O $conf_dir/override_fwknopd.conf --dump-config",
428 'category' => 'basic operations',
429 'subcategory' => 'client',
430 'detail' => '--get-key path validation',
431 'err_msg' => 'accepted improper --get-key path',
432 'function' => \&generic_exec,
433 'positive_output_matches' => [qr/could\snot\sopen/i],
435 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
436 "$fwknopCmd -A tcp/22 -s $fake_ip " .
437 "-D $loopback_ip --get-key not/there",
441 'category' => 'basic operations',
442 'subcategory' => 'client',
443 'detail' => 'require [-s|-R|-a]',
444 'err_msg' => 'allowed null allow IP',
445 'function' => \&generic_exec,
446 'positive_output_matches' => [qr/must\suse\sone\sof/i],
448 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
449 "$fwknopCmd -D $loopback_ip",
453 'category' => 'basic operations',
454 'subcategory' => 'client',
455 'detail' => '--allow-ip <IP> valid IP',
456 'err_msg' => 'permitted invalid --allow-ip arg',
457 'function' => \&generic_exec,
458 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
460 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
461 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
465 'category' => 'basic operations',
466 'subcategory' => 'client',
467 'detail' => '-A <proto>/<port> specification',
468 'err_msg' => 'permitted invalid -A <proto>/<port>',
469 'function' => \&generic_exec,
470 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
472 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
473 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
477 'category' => 'basic operations',
478 'subcategory' => 'client',
479 'detail' => 'generate SPA packet',
480 'err_msg' => 'could not generate SPA packet',
481 'function' => \&client_send_spa_packet,
482 'cmdline' => $default_client_args,
487 'category' => 'basic operations',
488 'subcategory' => 'server',
489 'detail' => 'list current fwknopd fw rules',
490 'err_msg' => 'could not list current fwknopd fw rules',
491 'function' => \&generic_exec,
492 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
493 "$fwknopdCmd $default_server_conf_args --fw-list",
497 'category' => 'basic operations',
498 'subcategory' => 'server',
499 'detail' => 'list all current fw rules',
500 'err_msg' => 'could not list all current fw rules',
501 'function' => \&generic_exec,
502 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
503 "$fwknopdCmd $default_server_conf_args --fw-list-all",
507 'category' => 'basic operations',
508 'subcategory' => 'server',
509 'detail' => 'flush current firewall rules',
510 'err_msg' => 'could not flush current fw rules',
511 'function' => \&generic_exec,
512 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
513 "$fwknopdCmd $default_server_conf_args --fw-flush",
518 'category' => 'basic operations',
519 'subcategory' => 'server',
521 'err_msg' => 'start error',
522 'function' => \&server_start,
523 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
524 "$fwknopdCmd $default_server_conf_args $intf_str",
528 'category' => 'basic operations',
529 'subcategory' => 'server',
531 'err_msg' => 'stop error',
532 'function' => \&server_stop,
533 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
534 "$fwknopdCmd $default_server_conf_args $intf_str",
538 'category' => 'basic operations',
539 'subcategory' => 'server',
540 'detail' => 'write PID',
541 'err_msg' => 'did not write PID',
542 'function' => \&write_pid,
543 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
544 "$fwknopdCmd $default_server_conf_args $intf_str",
549 'category' => 'basic operations',
550 'subcategory' => 'server',
551 'detail' => '--packet-limit 1 exit',
552 'err_msg' => 'did not exit after one packet',
553 'function' => \&server_packet_limit,
554 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
555 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
559 'category' => 'basic operations',
560 'subcategory' => 'server',
561 'detail' => 'ignore packets < min SPA len (140)',
562 'err_msg' => 'did not ignore small packets',
563 'function' => \&server_ignore_small_packets,
564 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
565 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
569 'category' => 'basic operations',
570 'subcategory' => 'server',
571 'detail' => '-P bpf filter ignore packet',
572 'err_msg' => 'filter did not ignore packet',
573 'function' => \&server_bpf_ignore_packet,
574 'cmdline' => $default_client_args,
575 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
576 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
577 qq|-P "udp port $non_std_spa_port"|,
582 'category' => 'Rijndael SPA',
583 'subcategory' => 'client+server',
584 'detail' => 'complete cycle (tcp/22 ssh)',
585 'err_msg' => 'could not complete SPA cycle',
586 'function' => \&spa_cycle,
587 'cmdline' => $default_client_args,
588 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
589 "$fwknopdCmd $default_server_conf_args $intf_str",
590 'fw_rule_created' => $NEW_RULE_REQUIRED,
591 'fw_rule_removed' => $NEW_RULE_REMOVED,
595 'category' => 'Rijndael SPA',
596 'subcategory' => 'client+server',
597 'detail' => 'dual usage access key (tcp/80 http)',
598 'err_msg' => 'could not complete SPA cycle',
599 'function' => \&spa_cycle,
600 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
601 "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
602 "$local_key_file --verbose --verbose",
603 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
604 "$fwknopdCmd -c $default_conf -a $dual_key_usage_access_conf " .
605 "-d $default_digest_file -p $default_pid_file $intf_str",
606 ### check for the first stanza that does not allow tcp/80 - the
607 ### second stanza allows this
608 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
609 'fw_rule_created' => $NEW_RULE_REQUIRED,
610 'fw_rule_removed' => $NEW_RULE_REMOVED,
614 'category' => 'Rijndael SPA',
615 'subcategory' => 'client+server',
616 'detail' => 'packet aging (past) (tcp/22 ssh)',
617 'err_msg' => 'old SPA packet accepted',
618 'function' => \&spa_cycle,
619 'cmdline' => "$default_client_args --time-offset-minus 300s",
620 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
621 "$fwknopdCmd $default_server_conf_args $intf_str",
622 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
623 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
627 'category' => 'Rijndael SPA',
628 'subcategory' => 'client+server',
629 'detail' => 'packet aging (future) (tcp/22 ssh)',
630 'err_msg' => 'future SPA packet accepted',
631 'function' => \&spa_cycle,
632 'cmdline' => "$default_client_args --time-offset-plus 300s",
633 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
634 "$fwknopdCmd $default_server_conf_args $intf_str",
635 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
636 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
640 'category' => 'Rijndael SPA',
641 'subcategory' => 'client+server',
642 'detail' => 'expired stanza (tcp/22 ssh)',
643 'err_msg' => 'SPA packet accepted',
644 'function' => \&spa_cycle,
645 'cmdline' => $default_client_args,
646 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
647 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
648 "-d $default_digest_file -p $default_pid_file $intf_str",
649 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
650 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
654 'category' => 'Rijndael SPA',
655 'subcategory' => 'client+server',
656 'detail' => 'invalid expire date (tcp/22 ssh)',
657 'err_msg' => 'SPA packet accepted',
658 'function' => \&spa_cycle,
659 'cmdline' => $default_client_args,
660 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
661 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
662 "-d $default_digest_file -p $default_pid_file $intf_str",
663 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
664 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
668 'category' => 'Rijndael SPA',
669 'subcategory' => 'client+server',
670 'detail' => 'expired epoch stanza (tcp/22 ssh)',
671 'err_msg' => 'SPA packet accepted',
672 'function' => \&spa_cycle,
673 'cmdline' => $default_client_args,
674 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
675 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
676 "-d $default_digest_file -p $default_pid_file $intf_str",
677 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
678 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
682 'category' => 'Rijndael SPA',
683 'subcategory' => 'client+server',
684 'detail' => 'future expired stanza (tcp/22 ssh)',
685 'err_msg' => 'SPA packet not accepted',
686 'function' => \&spa_cycle,
687 'cmdline' => $default_client_args,
688 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
689 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
690 "-d $default_digest_file -p $default_pid_file $intf_str",
691 'fw_rule_created' => $NEW_RULE_REQUIRED,
692 'fw_rule_removed' => $NEW_RULE_REMOVED,
697 'category' => 'Rijndael SPA',
698 'subcategory' => 'client+server',
699 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
700 'err_msg' => "improper OPEN_PORTS result",
701 'function' => \&spa_cycle,
702 'cmdline' => $default_client_args,
703 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
704 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
705 "-d $default_digest_file -p $default_pid_file $intf_str",
706 'fw_rule_created' => $NEW_RULE_REQUIRED,
707 'fw_rule_removed' => $NEW_RULE_REMOVED,
711 'category' => 'Rijndael SPA',
712 'subcategory' => 'client+server',
713 'detail' => 'OPEN_PORTS mismatch',
714 'err_msg' => "SPA packet accepted",
715 'function' => \&spa_cycle,
716 'cmdline' => $default_client_args,
717 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
718 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
719 "-d $default_digest_file -p $default_pid_file $intf_str",
720 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
721 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
725 'category' => 'Rijndael SPA',
726 'subcategory' => 'client+server',
727 'detail' => 'require user (tcp/22 ssh)',
728 'err_msg' => "missed require user criteria",
729 'function' => \&spa_cycle,
730 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
731 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
732 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
733 "-d $default_digest_file -p $default_pid_file $intf_str",
734 'fw_rule_created' => $NEW_RULE_REQUIRED,
735 'fw_rule_removed' => $NEW_RULE_REMOVED,
739 'category' => 'Rijndael SPA',
740 'subcategory' => 'client+server',
741 'detail' => 'user mismatch (tcp/22 ssh)',
742 'err_msg' => "improper user accepted for access",
743 'function' => \&user_mismatch,
744 'function' => \&spa_cycle,
745 'cmdline' => $default_client_args,
746 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
747 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
748 "-d $default_digest_file -p $default_pid_file $intf_str",
749 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
750 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
754 'category' => 'Rijndael SPA',
755 'subcategory' => 'client+server',
756 'detail' => 'require src (tcp/22 ssh)',
757 'err_msg' => "fw rule not created",
758 'function' => \&spa_cycle,
759 'cmdline' => $default_client_args,
760 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
761 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
762 "-d $default_digest_file -p $default_pid_file $intf_str",
763 'fw_rule_created' => $NEW_RULE_REQUIRED,
764 'fw_rule_removed' => $NEW_RULE_REMOVED,
768 'category' => 'Rijndael SPA',
769 'subcategory' => 'client+server',
770 'detail' => 'mismatch require src (tcp/22 ssh)',
771 'err_msg' => "fw rule created",
772 'function' => \&spa_cycle,
773 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
774 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
775 "$local_key_file --verbose --verbose",
776 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
777 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
778 "-d $default_digest_file -p $default_pid_file $intf_str",
779 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
780 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
785 'category' => 'Rijndael SPA',
786 'subcategory' => 'client+server',
787 'detail' => 'IP filtering (tcp/22 ssh)',
788 'err_msg' => "did not filter $loopback_ip",
789 'function' => \&spa_cycle,
790 'cmdline' => $default_client_args,
791 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
792 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
793 "-d $default_digest_file -p $default_pid_file $intf_str",
794 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
795 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
799 'category' => 'Rijndael SPA',
800 'subcategory' => 'client+server',
801 'detail' => 'subnet filtering (tcp/22 ssh)',
802 'err_msg' => "did not filter $loopback_ip",
803 'function' => \&spa_cycle,
804 'cmdline' => $default_client_args,
805 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
806 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
807 "-d $default_digest_file -p $default_pid_file $intf_str",
808 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
809 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
813 'category' => 'Rijndael SPA',
814 'subcategory' => 'client+server',
815 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
816 'err_msg' => "did not filter $loopback_ip",
817 'function' => \&spa_cycle,
818 'cmdline' => $default_client_args,
819 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
820 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
821 "-d $default_digest_file -p $default_pid_file $intf_str",
822 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
823 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
827 'category' => 'Rijndael SPA',
828 'subcategory' => 'client+server',
829 'detail' => 'IP match (tcp/22 ssh)',
830 'err_msg' => "did not filter $loopback_ip",
831 'function' => \&spa_cycle,
832 'cmdline' => $default_client_args,
833 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
834 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
835 "-d $default_digest_file -p $default_pid_file $intf_str",
836 'fw_rule_created' => $NEW_RULE_REQUIRED,
837 'fw_rule_removed' => $NEW_RULE_REMOVED,
841 'category' => 'Rijndael SPA',
842 'subcategory' => 'client+server',
843 'detail' => 'subnet match (tcp/22 ssh)',
844 'err_msg' => "did not filter $loopback_ip",
845 'function' => \&spa_cycle,
846 'cmdline' => $default_client_args,
847 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
848 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
849 "-d $default_digest_file -p $default_pid_file $intf_str",
850 'fw_rule_created' => $NEW_RULE_REQUIRED,
851 'fw_rule_removed' => $NEW_RULE_REMOVED,
855 'category' => 'Rijndael SPA',
856 'subcategory' => 'client+server',
857 'detail' => 'multi IP/net match (tcp/22 ssh)',
858 'err_msg' => "did not filter $loopback_ip",
859 'function' => \&spa_cycle,
860 'cmdline' => $default_client_args,
861 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
862 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
863 "-d $default_digest_file -p $default_pid_file $intf_str",
864 'fw_rule_created' => $NEW_RULE_REQUIRED,
865 'fw_rule_removed' => $NEW_RULE_REMOVED,
869 'category' => 'Rijndael SPA',
870 'subcategory' => 'client+server',
871 'detail' => 'multi access stanzas (tcp/22 ssh)',
872 'err_msg' => "could not complete SPA cycle",
873 'function' => \&spa_cycle,
874 'cmdline' => $default_client_args,
875 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
876 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
877 "-d $default_digest_file -p $default_pid_file $intf_str",
878 'fw_rule_created' => $NEW_RULE_REQUIRED,
879 'fw_rule_removed' => $NEW_RULE_REMOVED,
883 'category' => 'Rijndael SPA',
884 'subcategory' => 'client+server',
885 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
886 'err_msg' => "could not complete SPA cycle",
887 'function' => \&spa_cycle,
888 'cmdline' => $default_client_args,
889 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
890 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
891 "-d $default_digest_file -p $default_pid_file $intf_str",
892 'fw_rule_created' => $NEW_RULE_REQUIRED,
893 'fw_rule_removed' => $NEW_RULE_REMOVED,
898 'category' => 'Rijndael SPA',
899 'subcategory' => 'client+server',
900 'detail' => "non-enabled NAT (tcp/22 ssh)",
901 'err_msg' => "SPA packet not filtered",
902 'function' => \&spa_cycle,
903 'cmdline' => "$default_client_args -N $internal_nat_host:22",
904 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
905 "$fwknopdCmd $default_server_conf_args $intf_str",
906 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
907 'server_conf' => $nat_conf,
908 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
912 'category' => 'Rijndael SPA',
913 'subcategory' => 'client+server',
914 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
915 'err_msg' => "could not complete NAT SPA cycle",
916 'function' => \&spa_cycle,
917 'cmdline' => "$default_client_args -N $internal_nat_host:22",
918 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
919 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
920 "-d $default_digest_file -p $default_pid_file $intf_str",
921 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
922 'fw_rule_created' => $NEW_RULE_REQUIRED,
923 'fw_rule_removed' => $NEW_RULE_REMOVED,
924 'server_conf' => $nat_conf,
929 'category' => 'Rijndael SPA',
930 'subcategory' => 'client+server',
931 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
932 'err_msg' => "could not complete NAT SPA cycle",
933 'function' => \&spa_cycle,
934 'cmdline' => $default_client_args,
935 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
936 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
937 "-d $default_digest_file -p $default_pid_file $intf_str",
938 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
939 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
940 'fw_rule_created' => $NEW_RULE_REQUIRED,
941 'fw_rule_removed' => $NEW_RULE_REMOVED,
942 'server_conf' => $nat_conf,
947 'category' => 'Rijndael SPA',
948 'subcategory' => 'client+server',
949 'detail' => 'complete cycle (tcp/23 telnet)',
950 'err_msg' => 'could not complete SPA cycle',
951 'function' => \&spa_cycle,
952 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
953 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
954 "$local_key_file --verbose --verbose",
955 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
956 "$fwknopdCmd $default_server_conf_args $intf_str",
957 'fw_rule_created' => $NEW_RULE_REQUIRED,
958 'fw_rule_removed' => $NEW_RULE_REMOVED,
962 'category' => 'Rijndael SPA',
963 'subcategory' => 'client+server',
964 'detail' => 'complete cycle (tcp/9418 git)',
965 'err_msg' => 'could not complete SPA cycle',
966 'function' => \&spa_cycle,
967 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
968 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
969 "$local_key_file --verbose --verbose",
970 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
971 "$fwknopdCmd $default_server_conf_args $intf_str",
972 'fw_rule_created' => $NEW_RULE_REQUIRED,
973 'fw_rule_removed' => $NEW_RULE_REMOVED,
977 'category' => 'Rijndael SPA',
978 'subcategory' => 'client+server',
979 'detail' => 'complete cycle (udp/53 dns)',
980 'err_msg' => 'could not complete SPA cycle',
981 'function' => \&spa_cycle,
982 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
983 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
984 "$local_key_file --verbose --verbose",
985 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
986 "$fwknopdCmd $default_server_conf_args $intf_str",
987 'fw_rule_created' => $NEW_RULE_REQUIRED,
988 'fw_rule_removed' => $NEW_RULE_REMOVED,
992 'category' => 'Rijndael SPA',
993 'subcategory' => 'client+server',
994 'detail' => "-P bpf SPA over port $non_std_spa_port",
995 'err_msg' => 'could not complete SPA cycle',
996 'function' => \&spa_cycle,
997 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
998 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
999 "$fwknopdCmd $default_server_conf_args $intf_str " .
1000 qq|-P "udp port $non_std_spa_port"|,
1001 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1002 'fw_rule_created' => $NEW_RULE_REQUIRED,
1003 'fw_rule_removed' => $NEW_RULE_REMOVED,
1008 'category' => 'Rijndael SPA',
1009 'subcategory' => 'client+server',
1010 'detail' => 'random SPA port (tcp/22 ssh)',
1011 'err_msg' => 'could not complete SPA cycle',
1012 'function' => \&spa_cycle,
1013 'cmdline' => "$default_client_args -r",
1014 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1015 "$fwknopdCmd $default_server_conf_args $intf_str " .
1017 'fw_rule_created' => $NEW_RULE_REQUIRED,
1018 'fw_rule_removed' => $NEW_RULE_REMOVED,
1023 'category' => 'Rijndael SPA',
1024 'subcategory' => 'client+server',
1025 'detail' => 'spoof username (tcp/22)',
1026 'err_msg' => 'could not spoof username',
1027 'function' => \&spoof_username,
1028 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1029 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1030 "$local_key_file --verbose --verbose",
1031 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1032 "$fwknopdCmd $default_server_conf_args $intf_str",
1037 'category' => 'Rijndael SPA',
1038 'subcategory' => 'client+server',
1039 'detail' => 'replay attack detection',
1040 'err_msg' => 'could not detect replay attack',
1041 'function' => \&replay_detection,
1042 'cmdline' => $default_client_args,
1043 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1044 "$fwknopdCmd $default_server_conf_args $intf_str",
1048 'category' => 'Rijndael SPA',
1049 'subcategory' => 'server',
1050 'detail' => 'digest cache structure',
1051 'err_msg' => 'improper digest cache structure',
1052 'function' => \&digest_cache_structure,
1057 'category' => 'Rijndael SPA',
1058 'subcategory' => 'client+server',
1059 'detail' => 'non-base64 altered SPA data',
1060 'err_msg' => 'allowed improper SPA data',
1061 'function' => \&altered_non_base64_spa_data,
1062 'cmdline' => $default_client_args,
1063 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1064 "$fwknopdCmd $default_server_conf_args $intf_str",
1068 'category' => 'Rijndael SPA',
1069 'subcategory' => 'client+server',
1070 'detail' => 'base64 altered SPA data',
1071 'err_msg' => 'allowed improper SPA data',
1072 'function' => \&altered_base64_spa_data,
1073 'cmdline' => $default_client_args,
1074 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1075 "$fwknopdCmd $default_server_conf_args $intf_str",
1079 'category' => 'Rijndael SPA',
1080 'subcategory' => 'client+server',
1081 'detail' => 'appended data to SPA pkt',
1082 'err_msg' => 'allowed improper SPA data',
1083 'function' => \&appended_spa_data,
1084 'cmdline' => $default_client_args,
1085 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1086 "$fwknopdCmd $default_server_conf_args $intf_str",
1090 'category' => 'Rijndael SPA',
1091 'subcategory' => 'client+server',
1092 'detail' => 'prepended data to SPA pkt',
1093 'err_msg' => 'allowed improper SPA data',
1094 'function' => \&prepended_spa_data,
1095 'cmdline' => $default_client_args,
1096 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1097 "$fwknopdCmd $default_server_conf_args $intf_str",
1102 'category' => 'GnuPG (GPG) SPA',
1103 'subcategory' => 'client+server',
1104 'detail' => 'complete cycle (tcp/22 ssh)',
1105 'err_msg' => 'could not complete SPA cycle',
1106 'function' => \&spa_cycle,
1107 'cmdline' => $default_client_gpg_args,
1108 'fwknopd_cmdline' => $default_server_gpg_args,
1109 'fw_rule_created' => $NEW_RULE_REQUIRED,
1110 'fw_rule_removed' => $NEW_RULE_REMOVED,
1114 'category' => 'GnuPG (GPG) SPA',
1115 'subcategory' => 'client+server',
1116 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1117 'err_msg' => 'could not complete SPA cycle',
1118 'function' => \&spa_cycle,
1119 'cmdline' => $default_client_gpg_args,
1120 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1121 "$valgrind_str $fwknopdCmd -c $default_conf " .
1122 "-a $multi_gpg_access_conf $intf_str " .
1123 "-d $default_digest_file -p $default_pid_file",
1124 'fw_rule_created' => $NEW_RULE_REQUIRED,
1125 'fw_rule_removed' => $NEW_RULE_REMOVED,
1130 'category' => 'GnuPG (GPG) SPA',
1131 'subcategory' => 'client+server',
1132 'detail' => 'complete cycle (tcp/23 telnet)',
1133 'err_msg' => 'could not complete SPA cycle',
1134 'function' => \&spa_cycle,
1135 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1136 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1137 "$local_key_file --verbose --verbose " .
1138 "--gpg-recipient-key $gpg_server_key " .
1139 "--gpg-signer-key $gpg_client_key " .
1140 "--gpg-home-dir $gpg_client_home_dir",
1141 'fwknopd_cmdline' => $default_server_gpg_args,
1142 'fw_rule_created' => $NEW_RULE_REQUIRED,
1143 'fw_rule_removed' => $NEW_RULE_REMOVED,
1147 'category' => 'GnuPG (GPG) SPA',
1148 'subcategory' => 'client+server',
1149 'detail' => 'complete cycle (tcp/9418 git)',
1150 'err_msg' => 'could not complete SPA cycle',
1151 'function' => \&spa_cycle,
1152 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1153 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1154 "$local_key_file --verbose --verbose " .
1155 "--gpg-recipient-key $gpg_server_key " .
1156 "--gpg-signer-key $gpg_client_key " .
1157 "--gpg-home-dir $gpg_client_home_dir",
1158 'fwknopd_cmdline' => $default_server_gpg_args,
1159 'fw_rule_created' => $NEW_RULE_REQUIRED,
1160 'fw_rule_removed' => $NEW_RULE_REMOVED,
1164 'category' => 'GnuPG (GPG) SPA',
1165 'subcategory' => 'client+server',
1166 'detail' => 'complete cycle (udp/53 dns)',
1167 'err_msg' => 'could not complete SPA cycle',
1168 'function' => \&spa_cycle,
1169 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1170 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1171 "$local_key_file --verbose --verbose " .
1172 "--gpg-recipient-key $gpg_server_key " .
1173 "--gpg-signer-key $gpg_client_key " .
1174 "--gpg-home-dir $gpg_client_home_dir",
1175 'fwknopd_cmdline' => $default_server_gpg_args,
1176 'fw_rule_created' => $NEW_RULE_REQUIRED,
1177 'fw_rule_removed' => $NEW_RULE_REMOVED,
1182 'category' => 'GnuPG (GPG) SPA',
1183 'subcategory' => 'client+server',
1184 'detail' => 'replay attack detection',
1185 'err_msg' => 'could not detect replay attack',
1186 'function' => \&replay_detection,
1187 'cmdline' => $default_client_gpg_args,
1188 'fwknopd_cmdline' => $default_server_gpg_args,
1193 'category' => 'GnuPG (GPG) SPA',
1194 'subcategory' => 'client+server',
1195 'detail' => 'non-base64 altered SPA data',
1196 'err_msg' => 'allowed improper SPA data',
1197 'function' => \&altered_non_base64_spa_data,
1198 'cmdline' => $default_client_gpg_args,
1199 'fwknopd_cmdline' => $default_server_gpg_args,
1203 'category' => 'GnuPG (GPG) SPA',
1204 'subcategory' => 'client+server',
1205 'detail' => 'base64 altered SPA data',
1206 'err_msg' => 'allowed improper SPA data',
1207 'function' => \&altered_base64_spa_data,
1208 'cmdline' => $default_client_gpg_args,
1209 'fwknopd_cmdline' => $default_server_gpg_args,
1213 'category' => 'GnuPG (GPG) SPA',
1214 'subcategory' => 'client+server',
1215 'detail' => 'appended data to SPA pkt',
1216 'err_msg' => 'allowed improper SPA data',
1217 'function' => \&appended_spa_data,
1218 'cmdline' => $default_client_gpg_args,
1219 'fwknopd_cmdline' => $default_server_gpg_args,
1223 'category' => 'GnuPG (GPG) SPA',
1224 'subcategory' => 'client+server',
1225 'detail' => 'prepended data to SPA pkt',
1226 'err_msg' => 'allowed improper SPA data',
1227 'function' => \&prepended_spa_data,
1228 'cmdline' => $default_client_gpg_args,
1229 'fwknopd_cmdline' => $default_server_gpg_args,
1233 'category' => 'GnuPG (GPG) SPA',
1234 'subcategory' => 'client+server',
1235 'detail' => 'spoof username (tcp/22 ssh)',
1236 'err_msg' => 'could not spoof username',
1237 'function' => \&spoof_username,
1238 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1239 'fwknopd_cmdline' => $default_server_gpg_args,
1244 'category' => 'GnuPG (GPG) SPA',
1245 'subcategory' => 'server',
1246 'detail' => 'digest cache structure',
1247 'err_msg' => 'improper digest cache structure',
1248 'function' => \&digest_cache_structure,
1254 'category' => $REQUIRED,
1255 'subcategory' => $OPTIONAL,
1256 'detail' => $REQUIRED,
1257 'function' => $REQUIRED,
1258 'binary' => $OPTIONAL,
1259 'cmdline' => $OPTIONAL,
1260 'fwknopd_cmdline' => $OPTIONAL,
1261 'fatal' => $OPTIONAL,
1262 'exec_err' => $OPTIONAL,
1263 'fw_rule_created' => $OPTIONAL,
1264 'fw_rule_removed' => $OPTIONAL,
1265 'server_conf' => $OPTIONAL,
1266 'positive_output_matches' => $OPTIONAL,
1267 'negative_output_matches' => $OPTIONAL,
1268 'server_positive_output_matches' => $OPTIONAL,
1269 'server_negative_output_matches' => $OPTIONAL,
1273 &diff_test_results();
1277 ### make sure everything looks as expected before continuing
1280 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1281 " args: @args_cp\n\n"
1284 ### save the results from any previous test suite run
1285 ### so that we can potentially compare them with --diff
1286 if ($saved_last_results) {
1287 &logr(" Saved results from previous run " .
1288 "to: ${output_dir}.last/\n\n");
1291 ### main loop through all of the tests
1292 for my $test_hr (@tests) {
1293 &run_test($test_hr);
1296 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1298 copy $logfile, "$output_dir/$logfile" or die $!;
1302 #===================== end main =======================
1305 my $test_hr = shift;
1307 my $msg = "[$test_hr->{'category'}]";
1308 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1309 $msg .= " $test_hr->{'detail'}";
1311 return unless &process_include_exclude($msg);
1321 $current_test_file = "$output_dir/$executed.test";
1322 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1324 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1325 $test_hr->{'msg'} = $msg;
1326 if (&{$test_hr->{'function'}}($test_hr)) {
1327 &logr("pass ($executed)\n");
1330 &logr("fail ($executed)\n");
1333 if ($test_hr->{'fatal'} eq $YES) {
1334 die "[*] required test failed, exiting.";
1341 sub process_include_exclude() {
1344 ### inclusions/exclusions
1345 if (@tests_to_include) {
1347 for my $test (@tests_to_include) {
1348 if ($msg =~ /$test/) {
1353 return 0 unless $found;
1355 if (@tests_to_exclude) {
1357 for my $test (@tests_to_exclude) {
1358 if ($msg =~ /$test/) {
1368 sub diff_test_results() {
1369 die "[*] Need results from a previous run before running --diff"
1370 unless -d "${output_dir}.last";
1371 die "[*] Current results set does not exist." unless -d $output_dir;
1373 my %current_tests = ();
1374 my %previous_tests = ();
1376 ### Only diff results for matching tests (parse the logfile to see which
1377 ### test numbers match across the two test cycles).
1378 &build_results_hash(\%current_tests, $output_dir);
1379 &build_results_hash(\%previous_tests, "${output_dir}.last");
1381 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1382 keys %current_tests) {
1383 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1384 my $current_num = $current_tests{$test_msg}{'num'};
1385 if (defined $previous_tests{$test_msg}) {
1386 print "[+] Checking: $test_msg\n";
1387 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1388 my $previous_num = $previous_tests{$test_msg}{'num'};
1389 if ($current_result ne $previous_result) {
1390 print " DIFF: **$current_result** $test_msg\n";
1393 &diff_results($previous_num, $current_num);
1401 sub diff_results() {
1402 my ($previous_num, $current_num) = @_;
1404 ### edit out any valgrind "==354==" prefixes
1405 my $valgrind_search_re = qr/^==\d+==\s/;
1407 ### remove CMD timestamps
1408 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1410 for my $file ("${output_dir}.last/${previous_num}.test",
1411 "${output_dir}.last/${previous_num}_fwknopd.test",
1412 "${output_dir}/${current_num}.test",
1413 "${output_dir}/${current_num}_fwknopd.test",
1415 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1416 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1419 if (-e "${output_dir}.last/${previous_num}.test"
1420 and -e "${output_dir}/${current_num}.test") {
1421 system "diff -u ${output_dir}.last/${previous_num}.test " .
1422 "${output_dir}/${current_num}.test";
1425 if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1426 and -e "${output_dir}/${current_num}_fwknopd.test") {
1427 system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1428 "${output_dir}/${current_num}_fwknopd.test";
1434 sub build_results_hash() {
1435 my ($hr, $dir) = @_;
1437 open F, "< $dir/$logfile" or die $!;
1439 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1440 $hr->{$1}{'pass_fail'} = $2;
1441 $hr->{$1}{'num'} = $3;
1447 sub compile_warnings() {
1449 ### 'make clean' as root
1450 return 0 unless &run_cmd('make -C .. clean',
1451 $cmd_out_tmp, $current_test_file);
1454 my $username = getpwuid((stat($configure_path))[4]);
1455 die "[*] Could not determine $configure_path owner"
1458 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1459 $cmd_out_tmp, $current_test_file);
1463 return 0 unless &run_cmd('make -C ..',
1464 $cmd_out_tmp, $current_test_file);
1468 ### look for compilation warnings - something like:
1469 ### warning: ‘test’ is used uninitialized in this function
1470 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/], $current_test_file);
1472 ### the new binaries should exist
1473 unless (-e $fwknopCmd and -x $fwknopCmd) {
1474 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1475 $current_test_file);
1477 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1478 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1479 $current_test_file);
1485 sub binary_exists() {
1486 my $test_hr = shift;
1487 return 0 unless $test_hr->{'binary'};
1489 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1490 ### libfko.so link on OpenBSD)
1492 if ($test_hr->{'binary'} =~ /libfko/) {
1493 unless (-e $test_hr->{'binary'}) {
1494 for my $file (glob("$lib_dir/libfko.so*")) {
1495 if (-e $file and -x $file) {
1496 $test_hr->{'binary'} = $file;
1497 $libfko_bin = $file;
1504 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1508 sub expected_code_version() {
1509 my $test_hr = shift;
1511 unless (-e '../VERSION') {
1512 &write_test_file("[-] ../VERSION file does not exist.\n",
1513 $current_test_file);
1517 open F, '< ../VERSION' or die $!;
1520 if ($line =~ /(\d.*\d)/) {
1522 return 0 unless &run_cmd($test_hr->{'cmdline'},
1523 $cmd_out_tmp, $current_test_file);
1524 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1529 sub client_send_spa_packet() {
1530 my $test_hr = shift;
1532 &write_key('fwknoptest', $local_key_file);
1534 return 0 unless &run_cmd($test_hr->{'cmdline'},
1535 $cmd_out_tmp, $current_test_file);
1536 return 0 unless &file_find_regex([qr/final\spacked/i],
1537 $current_test_file);
1543 my $test_hr = shift;
1545 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1546 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1548 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1549 $rv = 0 unless $fw_rule_created;
1550 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1551 $rv = 0 if $fw_rule_created;
1554 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1555 $rv = 0 unless $fw_rule_removed;
1556 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1557 $rv = 0 if $fw_rule_removed;
1560 if ($test_hr->{'server_positive_output_matches'}) {
1561 $rv = 0 unless &file_find_regex(
1562 $test_hr->{'server_positive_output_matches'},
1566 if ($test_hr->{'server_negative_output_matches'}) {
1567 $rv = 0 if &file_find_regex(
1568 $test_hr->{'server_negative_output_matches'},
1575 sub spoof_username() {
1576 my $test_hr = shift;
1578 my $rv = &spa_cycle($test_hr);
1580 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1581 $current_test_file)) {
1585 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1586 $server_test_file)) {
1593 sub replay_detection() {
1594 my $test_hr = shift;
1596 ### do a complete SPA cycle and then parse the SPA packet out of the
1597 ### current test file and re-send
1599 return 0 unless &spa_cycle($test_hr);
1601 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1604 &write_test_file("[-] could not get SPA packet " .
1605 "from file: $current_test_file\n",
1606 $current_test_file);
1613 'port' => $default_spa_port,
1614 'dst_ip' => $loopback_ip,
1619 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1620 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1622 $rv = 0 unless $server_was_stopped;
1624 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1625 $server_test_file)) {
1632 sub digest_cache_structure() {
1633 my $test_hr = shift;
1636 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1638 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1640 ### the format should be:
1641 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1642 open F, "< $default_digest_file" or
1643 die "[*] could not open $default_digest_file: $!";
1647 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1648 &write_test_file("[-] invalid digest.cache line: $_",
1649 $current_test_file);
1655 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1656 &write_test_file("[+] DBM digest file format, " .
1657 "assuming this is valid.\n", $current_test_file);
1659 ### don't know what kind of file the digest.cache is
1660 &write_test_file("[-] unrecognized file type for " .
1661 "$default_digest_file.\n", $current_test_file);
1666 &write_test_file("[+] valid digest.cache structure.\n",
1667 $current_test_file);
1673 sub server_bpf_ignore_packet() {
1674 my $test_hr = shift;
1677 my $server_was_stopped = 0;
1678 my $fw_rule_created = 0;
1679 my $fw_rule_removed = 0;
1681 unless (&client_send_spa_packet($test_hr)) {
1682 &write_test_file("[-] fwknop client execution error.\n",
1683 $current_test_file);
1687 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1690 &write_test_file("[-] could not get SPA packet " .
1691 "from file: $current_test_file\n", $current_test_file);
1698 'port' => $default_spa_port,
1699 'dst_ip' => $loopback_ip,
1704 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1705 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1707 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1708 $server_test_file)) {
1715 sub altered_non_base64_spa_data() {
1716 my $test_hr = shift;
1719 my $server_was_stopped = 0;
1720 my $fw_rule_created = 0;
1721 my $fw_rule_removed = 0;
1723 unless (&client_send_spa_packet($test_hr)) {
1724 &write_test_file("[-] fwknop client execution error.\n",
1725 $current_test_file);
1729 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1732 &write_test_file("[-] could not get SPA packet " .
1733 "from file: $current_test_file\n", $current_test_file);
1737 ### alter one byte (change to a ":")
1738 $spa_pkt =~ s|^(.{3}).|$1:|;
1743 'port' => $default_spa_port,
1744 'dst_ip' => $loopback_ip,
1749 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1750 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1752 $rv = 0 unless $server_was_stopped;
1757 sub altered_base64_spa_data() {
1758 my $test_hr = shift;
1761 my $server_was_stopped = 0;
1762 my $fw_rule_created = 0;
1763 my $fw_rule_removed = 0;
1765 unless (&client_send_spa_packet($test_hr)) {
1766 &write_test_file("[-] fwknop client execution error.\n",
1767 $current_test_file);
1771 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1774 &write_test_file("[-] could not get SPA packet " .
1775 "from file: $current_test_file\n", $current_test_file);
1779 $spa_pkt =~ s|^(.{3}).|AAAA|;
1784 'port' => $default_spa_port,
1785 'dst_ip' => $loopback_ip,
1790 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1791 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1793 $rv = 0 unless $server_was_stopped;
1795 if ($fw_rule_created) {
1796 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1799 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1802 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1803 $server_test_file)) {
1810 sub appended_spa_data() {
1811 my $test_hr = shift;
1814 my $server_was_stopped = 0;
1815 my $fw_rule_created = 0;
1816 my $fw_rule_removed = 0;
1818 unless (&client_send_spa_packet($test_hr)) {
1819 &write_test_file("[-] fwknop client execution error.\n",
1820 $current_test_file);
1824 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1827 &write_test_file("[-] could not get SPA packet " .
1828 "from file: $current_test_file\n", $current_test_file);
1837 'port' => $default_spa_port,
1838 'dst_ip' => $loopback_ip,
1843 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1844 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1846 $rv = 0 unless $server_was_stopped;
1848 if ($fw_rule_created) {
1849 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1852 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1855 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1856 $server_test_file)) {
1863 sub prepended_spa_data() {
1864 my $test_hr = shift;
1867 my $server_was_stopped = 0;
1868 my $fw_rule_created = 0;
1869 my $fw_rule_removed = 0;
1871 unless (&client_send_spa_packet($test_hr)) {
1872 &write_test_file("[-] fwknop client execution error.\n",
1873 $current_test_file);
1877 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1880 &write_test_file("[-] could not get SPA packet " .
1881 "from file: $current_test_file\n", $current_test_file);
1885 $spa_pkt = 'AAAA' . $spa_pkt;
1890 'port' => $default_spa_port,
1891 'dst_ip' => $loopback_ip,
1896 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1897 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1899 $rv = 0 unless $server_was_stopped;
1901 if ($fw_rule_created) {
1902 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1905 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1908 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1909 $server_test_file)) {
1916 sub server_start() {
1917 my $test_hr = shift;
1919 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1920 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1922 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1923 $server_test_file)) {
1927 $rv = 0 unless $server_was_stopped;
1933 my $test_hr = shift;
1935 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1936 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1938 $rv = 0 unless $server_was_stopped;
1943 sub server_packet_limit() {
1944 my $test_hr = shift;
1949 'port' => $default_spa_port,
1950 'dst_ip' => $loopback_ip,
1955 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1956 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1958 if (&is_fwknopd_running()) {
1963 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
1964 $server_test_file)) {
1968 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
1969 $server_test_file)) {
1976 sub server_ignore_small_packets() {
1977 my $test_hr = shift;
1982 'port' => $default_spa_port,
1983 'dst_ip' => $loopback_ip,
1984 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
1988 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1989 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1993 if (&is_fwknopd_running()) {
2001 sub client_server_interaction() {
2002 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2005 my $server_was_stopped = 1;
2006 my $fw_rule_created = 1;
2007 my $fw_rule_removed = 0;
2009 ### start fwknopd to monitor for the SPA packet over the loopback interface
2010 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2012 ### give fwknopd a chance to parse its config and start sniffing
2013 ### on the loopback interface
2014 if ($use_valgrind) {
2020 ### send the SPA packet(s) to the server either manually using IO::Socket or
2021 ### with the fwknopd client
2022 if ($spa_client_flag == $USE_CLIENT) {
2023 unless (&client_send_spa_packet($test_hr)) {
2024 &write_test_file("[-] fwknop client execution error.\n",
2025 $current_test_file);
2029 &send_packets($pkts_hr);
2032 ### check to see if the SPA packet resulted in a new fw access rule
2034 while (not &is_fw_rule_active($test_hr)) {
2035 &write_test_file("[-] new fw rule does not exist.\n",
2036 $current_test_file);
2042 $fw_rule_created = 0;
2043 $fw_rule_removed = 0;
2046 &time_for_valgrind() if $use_valgrind;
2048 if ($fw_rule_created) {
2049 sleep 3; ### allow time for rule time out.
2050 if (&is_fw_rule_active($test_hr)) {
2051 &write_test_file("[-] new fw rule not timed out.\n",
2052 $current_test_file);
2055 &write_test_file("[+] new fw rule timed out.\n",
2056 $current_test_file);
2057 $fw_rule_removed = 1;
2061 if (&is_fwknopd_running()) {
2063 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2064 $server_test_file)) {
2065 $server_was_stopped = 0;
2068 &write_test_file("[-] server is not running.\n",
2069 $current_test_file);
2070 $server_was_stopped = 0;
2073 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2076 sub get_spa_packet_from_file() {
2081 my $found_trigger_line = 0;
2082 open F, "< $file" or die "[*] Could not open file $file: $!";
2084 if (/final\spacked/i) {
2085 $found_trigger_line = 1;
2088 next unless $found_trigger_line;
2090 ### the next line with non whitespace is the SPA packet
2101 sub send_packets() {
2102 my $pkts_ar = shift;
2104 open F, ">> $current_test_file" or die $!;
2105 print F "[+] send_packets(): Sending the following packets...\n";
2106 print F Dumper $pkts_ar;
2109 for my $pkt_hr (@$pkts_ar) {
2110 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2111 my $socket = IO::Socket::INET->new(
2112 PeerAddr => $pkt_hr->{'dst_ip'},
2113 PeerPort => $pkt_hr->{'port'},
2114 Proto => $pkt_hr->{'proto'},
2116 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2117 "socket to $pkt_hr->{'dst_ip'}: $!";
2119 $socket->send($pkt_hr->{'data'});
2122 } elsif ($pkt_hr->{'proto'} eq 'http') {
2124 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2128 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2133 sub generic_exec() {
2134 my $test_hr = shift;
2138 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2139 $cmd_out_tmp, $current_test_file);
2141 if ($test_hr->{'exec_err'} eq $YES) {
2142 $rv = 0 if $exec_rv;
2144 $rv = 0 unless $exec_rv;
2147 if ($test_hr->{'positive_output_matches'}) {
2148 $rv = 0 unless &file_find_regex(
2149 $test_hr->{'positive_output_matches'},
2150 $current_test_file);
2153 if ($test_hr->{'negative_output_matches'}) {
2154 $rv = 0 if &file_find_regex(
2155 $test_hr->{'negative_output_matches'},
2156 $current_test_file);
2164 my $test_hr = shift;
2165 return 0 unless $test_hr->{'binary'};
2166 &run_cmd("./hardening-check $test_hr->{'binary'}",
2167 $cmd_out_tmp, $current_test_file);
2168 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2169 $current_test_file);
2173 ### check for stack protection
2174 sub stack_protected_binary() {
2175 my $test_hr = shift;
2176 return 0 unless $test_hr->{'binary'};
2177 &run_cmd("./hardening-check $test_hr->{'binary'}",
2178 $cmd_out_tmp, $current_test_file);
2179 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2180 $current_test_file);
2184 ### check for fortified source functions
2185 sub fortify_source_functions() {
2186 my $test_hr = shift;
2187 return 0 unless $test_hr->{'binary'};
2188 &run_cmd("./hardening-check $test_hr->{'binary'}",
2189 $cmd_out_tmp, $current_test_file);
2190 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2191 $current_test_file);
2195 ### check for read-only relocations
2196 sub read_only_relocations() {
2197 my $test_hr = shift;
2198 return 0 unless $test_hr->{'binary'};
2199 &run_cmd("./hardening-check $test_hr->{'binary'}",
2200 $cmd_out_tmp, $current_test_file);
2201 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2202 $current_test_file);
2206 ### check for immediate binding
2207 sub immediate_binding() {
2208 my $test_hr = shift;
2209 return 0 unless $test_hr->{'binary'};
2210 &run_cmd("./hardening-check $test_hr->{'binary'}",
2211 $cmd_out_tmp, $current_test_file);
2212 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2213 $current_test_file);
2219 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2220 "$default_server_conf_args --fw-list-all",
2221 $cmd_out_tmp, $current_test_file);
2229 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2230 'if [ `which iptables` ]; then iptables -V; fi',
2231 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2232 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2233 'if [ `which gpg` ]; then gpg --version; fi',
2234 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2238 'ls -l /usr/lib/*pcap*',
2239 'ls -l /usr/local/lib/*pcap*',
2240 'ls -l /usr/lib/*fko*',
2241 'ls -l /usr/local/lib/*fko*',
2243 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2245 if ($cmd =~ /^ldd/) {
2246 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2250 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2251 ### to enable gpg tests
2252 unless ($have_gpgme == 3) {
2253 push @tests_to_exclude, "GPG";
2259 sub time_for_valgrind() {
2261 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2262 "grep valgrind |grep -v perl | grep -v grep",
2263 $cmd_out_tmp, $current_test_file)) {
2271 sub anonymize_results() {
2273 die "[*] $output_dir does not exist" unless -d $output_dir;
2274 die "[*] $logfile does not exist, has $0 been executed?"
2277 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2280 ### remove non-loopback IP addresses
2281 my $search_re = qr/\b127\.0\.0\.1\b/;
2282 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2283 $search_re = qr/\b127\.0\.0\.2\b/;
2284 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2285 $search_re = qr/\b0\.0\.0\.0\b/;
2286 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2287 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2288 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2289 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2290 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2291 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2293 ### remove hostname from any uname output
2294 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2295 system "perl -p -i -e 'undef \$/; s|$search_re" .
2296 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2298 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2299 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2302 system "tar cvfz $tarfile $logfile $output_dir";
2303 print "[+] Anonymized test results file: $tarfile\n";
2312 my $test_hr = shift;
2314 open F, "> $default_pid_file" or die $!;
2318 &server_start($test_hr);
2320 open F, "< $default_pid_file" or die $!;
2332 sub start_fwknopd() {
2333 my $test_hr = shift;
2335 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2338 die "[*] Could not fork: $!" unless defined $pid;
2342 ### we are the child, so start fwknopd
2343 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2344 $server_cmd_tmp, $server_test_file);
2350 my ($key, $file) = @_;
2352 open K, "> $file" or die "[*] Could not open $file: $!";
2353 print K "$loopback_ip: $key\n";
2354 print K "localhost: $key\n";
2355 print K "some.host.through.proxy.com: $key\n";
2361 open C, ">> $current_test_file"
2362 or die "[*] Could not open $current_test_file: $!";
2363 print C "\n" . localtime() . " [+] PID dump:\n";
2365 &run_cmd("ps auxww | grep knop |grep -v grep",
2366 $cmd_out_tmp, $current_test_file);
2371 my ($cmd, $cmd_out, $file) = @_;
2375 or die "[*] Could not open $file: $!";
2376 print F localtime() . " CMD: $cmd\n";
2380 or die "[*] Could not open $file: $!";
2381 print F localtime() . " CMD: $cmd\n";
2385 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2387 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2388 my @cmd_lines = <C>;
2391 open F, ">> $file" or die "[*] Could not open $file: $!";
2392 print F $_ for @cmd_lines;
2405 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2414 $|++; ### turn off buffering
2416 $< == 0 && $> == 0 or
2417 die "[*] $0: You must be root (or equivalent ",
2418 "UID 0 account) to effectively test fwknop";
2420 ### validate test hashes
2422 for my $test_hr (@tests) {
2423 for my $key (keys %test_keys) {
2424 if ($test_keys{$key} == $REQUIRED) {
2425 die "[*] Missing '$key' element in hash: $hash_num"
2426 unless defined $test_hr->{$key};
2428 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2434 if ($use_valgrind) {
2435 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2436 unless -e $valgrindCmd and -x $valgrindCmd;
2439 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2440 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2442 for my $file ($configure_path,
2445 $default_access_conf,
2446 $no_source_match_access_conf,
2447 $ip_source_match_access_conf,
2448 $subnet_source_match_access_conf,
2449 $no_subnet_source_match_access_conf,
2450 $no_multi_source_match_access_conf,
2451 $multi_source_match_access_conf,
2452 $open_ports_access_conf,
2453 $mismatch_open_ports_access_conf,
2454 $require_user_access_conf,
2455 $mismatch_user_access_conf,
2456 $require_src_access_conf,
2457 $multi_gpg_access_conf,
2458 $multi_stanzas_access_conf,
2459 $expired_access_conf,
2460 $expired_epoch_access_conf,
2461 $future_expired_access_conf,
2462 $invalid_expire_access_conf,
2463 $force_nat_access_conf,
2465 die "[*] $file does not exist" unless -e $file;
2468 if (-d $output_dir) {
2469 if (-d "${output_dir}.last") {
2470 rmtree "${output_dir}.last"
2471 or die "[*] rmtree ${output_dir}.last $!";
2473 mkdir "${output_dir}.last"
2474 or die "[*] ${output_dir}.last: $!";
2475 for my $file (glob("$output_dir/*.test")) {
2476 if ($file =~ m|.*/(.*)|) {
2477 copy $file, "${output_dir}.last/$1" or die $!;
2480 if (-e "$output_dir/init") {
2481 copy "$output_dir/init", "${output_dir}.last/init";
2484 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2486 $saved_last_results = 1;
2488 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2490 unless (-d $run_dir) {
2491 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2494 for my $file (glob("$output_dir/*.test")) {
2495 unlink $file or die "[*] Could not unlink($file)";
2497 if (-e "$output_dir/init") {
2498 unlink "$output_dir/init" or die $!;
2502 unlink $logfile or die $!;
2505 if ($test_include) {
2506 @tests_to_include = split /\s*,\s*/, $test_include;
2508 if ($test_exclude) {
2509 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2512 ### make sure no fwknopd instance is currently running
2513 die "[*] Please stop the running fwknopd instance."
2514 if &is_fwknopd_running();
2516 unless ($enable_recompilation_warnings_check) {
2517 push @tests_to_exclude, 'recompilation';
2520 $sudo_path = &find_command('sudo');
2522 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2523 ### disable compilation checks
2524 push @tests_to_exclude, 'recompilation';
2527 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2530 $platform = 'linux';
2536 unless ($platform eq 'linux') {
2537 push @tests_to_exclude, 'NAT';
2543 sub identify_loopback_intf() {
2544 return if $loopback_intf;
2548 ### lo Link encap:Local Loopback
2549 ### inet addr:127.0.0.1 Mask:255.0.0.0
2550 ### inet6 addr: ::1/128 Scope:Host
2551 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2552 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2553 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2554 ### collisions:0 txqueuelen:0
2555 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2559 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2560 ### options=3<RXCSUM,TXCSUM>
2561 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2562 ### inet6 ::1 prefixlen 128
2563 ### inet 127.0.0.1 netmask 0xff000000
2564 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2567 my $found_loopback_intf = 0;
2569 my $cmd = 'ifconfig -a';
2570 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2572 if (/^(\S+?):?\s+.*loopback/i) {
2576 if (/^\S/ and $intf and not $found_loopback_intf) {
2577 ### should not happen
2580 if ($intf and /\b127\.0\.0\.1\b/) {
2581 $found_loopback_intf = 1;
2587 die "[*] could not determine loopback interface, use --loopback <name>"
2588 unless $found_loopback_intf;
2590 $loopback_intf = $intf;
2595 sub is_fw_rule_active() {
2596 my $test_hr = shift;
2598 my $conf_args = $default_server_conf_args;
2600 if ($test_hr->{'server_conf'}) {
2601 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2602 "-d $default_digest_file -p $default_pid_file";
2605 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2606 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2607 $cmd_out_tmp, $current_test_file);
2611 sub is_fwknopd_running() {
2613 sleep 2 if $use_valgrind;
2615 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2616 "--status", $cmd_out_tmp, $current_test_file);
2618 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2623 sub stop_fwknopd() {
2625 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2626 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2628 if ($use_valgrind) {
2629 &time_for_valgrind();
2637 sub file_find_regex() {
2638 my ($re_ar, $file) = @_;
2641 my @write_lines = ();
2643 open F, "< $file" or die "[*] Could not open $file: $!";
2646 next LINE if $line =~ /file_file_regex\(\)/;
2647 for my $re (@$re_ar) {
2649 push @write_lines, "[.] file_find_regex() " .
2650 "Matched '$re' with line: $line";
2659 for my $line (@write_lines) {
2660 &write_test_file($line, $file);
2663 &write_test_file("[.] find_find_regex() Did not " .
2664 "match any regex in: '@$re_ar'\n", $file);
2670 sub find_command() {
2674 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2676 if (m|^(/.*$cmd)$|) {
2685 sub write_test_file() {
2686 my ($msg, $file) = @_;
2690 or die "[*] Could not open $file: $!";
2695 or die "[*] Could not open $file: $!";
2705 open F, ">> $logfile" or die $!;