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 $ecb_mode_access_conf = "$conf_dir/ecb_mode_access.conf";
26 my $ctr_mode_access_conf = "$conf_dir/ctr_mode_access.conf";
27 my $cfb_mode_access_conf = "$conf_dir/cfb_mode_access.conf";
28 my $ofb_mode_access_conf = "$conf_dir/ofb_mode_access.conf";
29 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
30 my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
31 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
32 my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
33 my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
34 my $gpg_access_conf = "$conf_dir/gpg_access.conf";
35 my $default_digest_file = "$run_dir/digest.cache";
36 my $default_pid_file = "$run_dir/fwknopd.pid";
37 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
38 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
39 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
40 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
41 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
42 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
43 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
44 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
45 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
46 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
47 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
48 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
49 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
50 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
52 my $fwknopCmd = '../client/.libs/fwknop';
53 my $fwknopdCmd = '../server/.libs/fwknopd';
54 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
55 my $valgrindCmd = '/usr/bin/valgrind';
57 my $gpg_server_key = '361BBAD4';
58 my $gpg_client_key = '6A3FAD56';
60 my $loopback_ip = '127.0.0.1';
61 my $fake_ip = '127.0.0.2';
62 my $internal_nat_host = '192.168.1.2';
63 my $force_nat_host = '192.168.1.123';
64 my $default_spa_port = 62201;
65 my $non_std_spa_port = 12345;
67 my $spoof_user = 'testuser';
68 #================== end config ===================
73 my $test_include = '';
74 my @tests_to_include = ();
75 my $test_exclude = '';
76 my @tests_to_exclude = ();
78 my $loopback_intf = '';
79 my $anonymize_results = 0;
80 my $current_test_file = "$output_dir/init";
81 my $tarfile = 'test_fwknop.tar.gz';
82 my $server_test_file = '';
84 my $valgrind_str = '';
85 my $saved_last_results = 0;
87 my $enable_recompilation_warnings_check = 0;
94 my $USE_PREDEF_PKTS = 1;
98 my $NEW_RULE_REQUIRED = 1;
99 my $REQUIRE_NO_NEW_RULE = 2;
100 my $NEW_RULE_REMOVED = 1;
101 my $REQUIRE_NO_NEW_REMOVED = 2;
103 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
107 exit 1 unless GetOptions(
108 'Anonymize-results' => \$anonymize_results,
109 'fwknop-path=s' => \$fwknopCmd,
110 'fwknopd-path=s' => \$fwknopdCmd,
111 'libfko-path=s' => \$libfko_bin,
112 'loopback-intf=s' => \$loopback_intf,
113 'test-include=s' => \$test_include,
114 'include=s' => \$test_include, ### synonym
115 'test-exclude=s' => \$test_exclude,
116 'exclude=s' => \$test_exclude, ### synonym
117 'enable-recompile-check' => \$enable_recompilation_warnings_check,
118 'List-mode' => \$list_mode,
119 'enable-valgrind' => \$use_valgrind,
120 'valgrind-path=s' => \$valgrindCmd,
121 'diff' => \$diff_mode,
127 ### create an anonymized tar file of test suite results that can be
128 ### emailed around to assist in debugging fwknop communications
129 exit &anonymize_results() if $anonymize_results;
131 &identify_loopback_intf();
133 $valgrind_str = "$valgrindCmd --leak-check=full " .
134 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
136 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
138 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
139 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
140 "$local_key_file --verbose --verbose";
142 my $default_client_gpg_args = "$default_client_args " .
143 "--gpg-recipient-key $gpg_server_key " .
144 "--gpg-signer-key $gpg_client_key " .
145 "--gpg-home-dir $gpg_client_home_dir";
147 my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
148 "-d $default_digest_file -p $default_pid_file";
150 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
151 "$valgrind_str $fwknopdCmd -c $default_conf " .
152 "-a $gpg_access_conf $intf_str " .
153 "-d $default_digest_file -p $default_pid_file";
155 ### point the compiled binaries at the local libary path
156 ### instead of any installed libfko instance
157 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
159 ### main array that defines the tests we will run
162 'category' => 'recompilation',
163 'detail' => 'recompile and look for compilation warnings',
164 'err_msg' => 'compile warnings exist',
165 'function' => \&compile_warnings,
169 'category' => 'build',
170 'subcategory' => 'client',
171 'detail' => 'binary exists',
172 'err_msg' => 'binary not found',
173 'function' => \&binary_exists,
174 'binary' => $fwknopCmd,
178 'category' => 'build security',
179 'subcategory' => 'client',
180 'detail' => 'Position Independent Executable (PIE)',
181 'err_msg' => 'non PIE binary (fwknop client)',
182 'function' => \&pie_binary,
183 'binary' => $fwknopCmd,
187 'category' => 'build security',
188 'subcategory' => 'client',
189 'detail' => 'stack protected binary',
190 'err_msg' => 'non stack protected binary (fwknop client)',
191 'function' => \&stack_protected_binary,
192 'binary' => $fwknopCmd,
196 'category' => 'build security',
197 'subcategory' => 'client',
198 'detail' => 'fortify source functions',
199 'err_msg' => 'source functions not fortified (fwknop client)',
200 'function' => \&fortify_source_functions,
201 'binary' => $fwknopCmd,
205 'category' => 'build security',
206 'subcategory' => 'client',
207 'detail' => 'read-only relocations',
208 'err_msg' => 'no read-only relocations (fwknop client)',
209 'function' => \&read_only_relocations,
210 'binary' => $fwknopCmd,
214 'category' => 'build security',
215 'subcategory' => 'client',
216 'detail' => 'immediate binding',
217 'err_msg' => 'no immediate binding (fwknop client)',
218 'function' => \&immediate_binding,
219 'binary' => $fwknopCmd,
224 'category' => 'build',
225 'subcategory' => 'server',
226 'detail' => 'binary exists',
227 'err_msg' => 'binary not found',
228 'function' => \&binary_exists,
229 'binary' => $fwknopdCmd,
234 'category' => 'build security',
235 'subcategory' => 'server',
236 'detail' => 'Position Independent Executable (PIE)',
237 'err_msg' => 'non PIE binary (fwknopd server)',
238 'function' => \&pie_binary,
239 'binary' => $fwknopdCmd,
243 'category' => 'build security',
244 'subcategory' => 'server',
245 'detail' => 'stack protected binary',
246 'err_msg' => 'non stack protected binary (fwknopd server)',
247 'function' => \&stack_protected_binary,
248 'binary' => $fwknopdCmd,
252 'category' => 'build security',
253 'subcategory' => 'server',
254 'detail' => 'fortify source functions',
255 'err_msg' => 'source functions not fortified (fwknopd server)',
256 'function' => \&fortify_source_functions,
257 'binary' => $fwknopdCmd,
261 'category' => 'build security',
262 'subcategory' => 'server',
263 'detail' => 'read-only relocations',
264 'err_msg' => 'no read-only relocations (fwknopd server)',
265 'function' => \&read_only_relocations,
266 'binary' => $fwknopdCmd,
270 'category' => 'build security',
271 'subcategory' => 'server',
272 'detail' => 'immediate binding',
273 'err_msg' => 'no immediate binding (fwknopd server)',
274 'function' => \&immediate_binding,
275 'binary' => $fwknopdCmd,
280 'category' => 'build',
281 'subcategory' => 'libfko',
282 'detail' => 'binary exists',
283 'err_msg' => 'binary not found',
284 'function' => \&binary_exists,
285 'binary' => $libfko_bin,
289 'category' => 'build security',
290 'subcategory' => 'libfko',
291 'detail' => 'stack protected binary',
292 'err_msg' => 'non stack protected binary (libfko)',
293 'function' => \&stack_protected_binary,
294 'binary' => $libfko_bin,
298 'category' => 'build security',
299 'subcategory' => 'libfko',
300 'detail' => 'fortify source functions',
301 'err_msg' => 'source functions not fortified (libfko)',
302 'function' => \&fortify_source_functions,
303 'binary' => $libfko_bin,
307 'category' => 'build security',
308 'subcategory' => 'libfko',
309 'detail' => 'read-only relocations',
310 'err_msg' => 'no read-only relocations (libfko)',
311 'function' => \&read_only_relocations,
312 'binary' => $libfko_bin,
316 'category' => 'build security',
317 'subcategory' => 'libfko',
318 'detail' => 'immediate binding',
319 'err_msg' => 'no immediate binding (libfko)',
320 'function' => \&immediate_binding,
321 'binary' => $libfko_bin,
326 'category' => 'preliminaries',
327 'subcategory' => 'client',
328 'detail' => 'usage info',
329 'err_msg' => 'could not get usage info',
330 'function' => \&generic_exec,
331 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
335 'category' => 'preliminaries',
336 'subcategory' => 'client',
337 'detail' => 'getopt() no such argument',
338 'err_msg' => 'getopt() allowed non-existant argument',
339 'function' => \&generic_exec,
340 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
345 'category' => 'preliminaries',
346 'subcategory' => 'client',
347 'detail' => '--test mode, packet not sent',
348 'err_msg' => '--test mode, packet sent?',
349 'function' => \&generic_exec,
350 'positive_output_matches' => [qr/test\smode\senabled/],
351 'cmdline' => "$default_client_args --test",
356 'category' => 'preliminaries',
357 'subcategory' => 'client',
358 'detail' => 'expected code version',
359 'err_msg' => 'code version mis-match',
360 'function' => \&expected_code_version,
361 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
366 'category' => 'preliminaries',
367 'subcategory' => 'server',
368 'detail' => 'usage info',
369 'err_msg' => 'could not get usage info',
370 'function' => \&generic_exec,
371 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
375 'category' => 'preliminaries',
376 'subcategory' => 'server',
377 'detail' => 'getopt() no such argument',
378 'err_msg' => 'getopt() allowed non-existant argument',
379 'function' => \&generic_exec,
380 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
386 'category' => 'preliminaries',
387 'subcategory' => 'server',
388 'detail' => 'expected code version',
389 'err_msg' => 'code version mis-match',
390 'function' => \&expected_code_version,
391 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
392 "$fwknopdCmd -c $default_conf -a " .
393 "$default_access_conf --version",
397 'category' => 'preliminaries',
398 'detail' => 'collecting system specifics',
399 'err_msg' => 'could not get complete system specs',
400 'function' => \&specs,
401 'binary' => $fwknopdCmd,
406 'category' => 'basic operations',
407 'detail' => 'dump config',
408 'err_msg' => 'could not dump configuration',
409 'function' => \&generic_exec,
410 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
412 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
413 "$fwknopdCmd -c $default_conf " .
414 "-a $default_access_conf --dump-config",
418 'category' => 'basic operations',
419 'detail' => 'override config',
420 'err_msg' => 'could not override configuration',
421 'function' => \&generic_exec,
422 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
424 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
425 "$fwknopdCmd $default_server_conf_args " .
426 "-O $conf_dir/override_fwknopd.conf --dump-config",
431 'category' => 'basic operations',
432 'subcategory' => 'client',
433 'detail' => '--get-key path validation',
434 'err_msg' => 'accepted improper --get-key path',
435 'function' => \&generic_exec,
436 'positive_output_matches' => [qr/could\snot\sopen/i],
438 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
439 "$fwknopCmd -A tcp/22 -s $fake_ip " .
440 "-D $loopback_ip --get-key not/there",
444 'category' => 'basic operations',
445 'subcategory' => 'client',
446 'detail' => 'require [-s|-R|-a]',
447 'err_msg' => 'allowed null allow IP',
448 'function' => \&generic_exec,
449 'positive_output_matches' => [qr/must\suse\sone\sof/i],
451 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
452 "$fwknopCmd -D $loopback_ip",
456 'category' => 'basic operations',
457 'subcategory' => 'client',
458 'detail' => '--allow-ip <IP> valid IP',
459 'err_msg' => 'permitted invalid --allow-ip arg',
460 'function' => \&generic_exec,
461 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
463 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
464 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
468 'category' => 'basic operations',
469 'subcategory' => 'client',
470 'detail' => '-A <proto>/<port> specification',
471 'err_msg' => 'permitted invalid -A <proto>/<port>',
472 'function' => \&generic_exec,
473 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
475 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
476 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
480 'category' => 'basic operations',
481 'subcategory' => 'client',
482 'detail' => 'generate SPA packet',
483 'err_msg' => 'could not generate SPA packet',
484 'function' => \&client_send_spa_packet,
485 'cmdline' => $default_client_args,
490 'category' => 'basic operations',
491 'subcategory' => 'server',
492 'detail' => 'list current fwknopd fw rules',
493 'err_msg' => 'could not list current fwknopd fw rules',
494 'function' => \&generic_exec,
495 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
496 "$fwknopdCmd $default_server_conf_args --fw-list",
500 'category' => 'basic operations',
501 'subcategory' => 'server',
502 'detail' => 'list all current fw rules',
503 'err_msg' => 'could not list all current fw rules',
504 'function' => \&generic_exec,
505 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
506 "$fwknopdCmd $default_server_conf_args --fw-list-all",
510 'category' => 'basic operations',
511 'subcategory' => 'server',
512 'detail' => 'flush current firewall rules',
513 'err_msg' => 'could not flush current fw rules',
514 'function' => \&generic_exec,
515 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
516 "$fwknopdCmd $default_server_conf_args --fw-flush",
521 'category' => 'basic operations',
522 'subcategory' => 'server',
524 'err_msg' => 'start error',
525 'function' => \&server_start,
526 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
527 "$fwknopdCmd $default_server_conf_args $intf_str",
531 'category' => 'basic operations',
532 'subcategory' => 'server',
534 'err_msg' => 'stop error',
535 'function' => \&server_stop,
536 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
537 "$fwknopdCmd $default_server_conf_args $intf_str",
541 'category' => 'basic operations',
542 'subcategory' => 'server',
543 'detail' => 'write PID',
544 'err_msg' => 'did not write PID',
545 'function' => \&write_pid,
546 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
547 "$fwknopdCmd $default_server_conf_args $intf_str",
552 'category' => 'basic operations',
553 'subcategory' => 'server',
554 'detail' => '--packet-limit 1 exit',
555 'err_msg' => 'did not exit after one packet',
556 'function' => \&server_packet_limit,
557 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
558 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
562 'category' => 'basic operations',
563 'subcategory' => 'server',
564 'detail' => 'ignore packets < min SPA len (140)',
565 'err_msg' => 'did not ignore small packets',
566 'function' => \&server_ignore_small_packets,
567 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
568 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
572 'category' => 'basic operations',
573 'subcategory' => 'server',
574 'detail' => '-P bpf filter ignore packet',
575 'err_msg' => 'filter did not ignore packet',
576 'function' => \&server_bpf_ignore_packet,
577 'cmdline' => $default_client_args,
578 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
579 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
580 qq|-P "udp port $non_std_spa_port"|,
585 'category' => 'Rijndael SPA',
586 'subcategory' => 'client+server',
587 'detail' => 'complete cycle (tcp/22 ssh)',
588 'err_msg' => 'could not complete SPA cycle',
589 'function' => \&spa_cycle,
590 'cmdline' => $default_client_args,
591 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
592 "$fwknopdCmd $default_server_conf_args $intf_str",
593 'fw_rule_created' => $NEW_RULE_REQUIRED,
594 'fw_rule_removed' => $NEW_RULE_REMOVED,
598 'category' => 'Rijndael SPA',
599 'subcategory' => 'client+server',
600 'detail' => 'packet aging (past) (tcp/22 ssh)',
601 'err_msg' => 'old SPA packet accepted',
602 'function' => \&spa_cycle,
603 'cmdline' => "$default_client_args --time-offset-minus 300s",
604 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
605 "$fwknopdCmd $default_server_conf_args $intf_str",
606 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
607 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
611 'category' => 'Rijndael SPA',
612 'subcategory' => 'client+server',
613 'detail' => 'packet aging (future) (tcp/22 ssh)',
614 'err_msg' => 'future SPA packet accepted',
615 'function' => \&spa_cycle,
616 'cmdline' => "$default_client_args --time-offset-plus 300s",
617 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
618 "$fwknopdCmd $default_server_conf_args $intf_str",
619 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
620 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
624 'category' => 'Rijndael SPA',
625 'subcategory' => 'client+server',
626 'detail' => 'expired stanza (tcp/22 ssh)',
627 'err_msg' => 'SPA packet accepted',
628 'function' => \&spa_cycle,
629 'cmdline' => $default_client_args,
630 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
631 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
632 "-d $default_digest_file -p $default_pid_file $intf_str",
633 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
634 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
638 'category' => 'Rijndael SPA',
639 'subcategory' => 'client+server',
640 'detail' => 'invalid expire date (tcp/22 ssh)',
641 'err_msg' => 'SPA packet accepted',
642 'function' => \&spa_cycle,
643 'cmdline' => $default_client_args,
644 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
645 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
646 "-d $default_digest_file -p $default_pid_file $intf_str",
647 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
648 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
652 'category' => 'Rijndael SPA',
653 'subcategory' => 'client+server',
654 'detail' => 'expired epoch stanza (tcp/22 ssh)',
655 'err_msg' => 'SPA packet accepted',
656 'function' => \&spa_cycle,
657 'cmdline' => $default_client_args,
658 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
659 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
660 "-d $default_digest_file -p $default_pid_file $intf_str",
661 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
662 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
666 'category' => 'Rijndael SPA',
667 'subcategory' => 'client+server',
668 'detail' => 'future expired stanza (tcp/22 ssh)',
669 'err_msg' => 'SPA packet not accepted',
670 'function' => \&spa_cycle,
671 'cmdline' => $default_client_args,
672 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
673 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
674 "-d $default_digest_file -p $default_pid_file $intf_str",
675 'fw_rule_created' => $NEW_RULE_REQUIRED,
676 'fw_rule_removed' => $NEW_RULE_REMOVED,
681 'category' => 'Rijndael SPA',
682 'subcategory' => 'client+server',
683 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
684 'err_msg' => "improper OPEN_PORTS result",
685 'function' => \&spa_cycle,
686 'cmdline' => $default_client_args,
687 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
688 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
689 "-d $default_digest_file -p $default_pid_file $intf_str",
690 'fw_rule_created' => $NEW_RULE_REQUIRED,
691 'fw_rule_removed' => $NEW_RULE_REMOVED,
695 'category' => 'Rijndael SPA',
696 'subcategory' => 'client+server',
697 'detail' => 'OPEN_PORTS mismatch',
698 'err_msg' => "SPA packet accepted",
699 'function' => \&spa_cycle,
700 'cmdline' => $default_client_args,
701 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
702 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
703 "-d $default_digest_file -p $default_pid_file $intf_str",
704 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
705 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
709 'category' => 'Rijndael SPA',
710 'subcategory' => 'client+server',
711 'detail' => 'require user (tcp/22 ssh)',
712 'err_msg' => "missed require user criteria",
713 'function' => \&spa_cycle,
714 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
715 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
716 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
717 "-d $default_digest_file -p $default_pid_file $intf_str",
718 'fw_rule_created' => $NEW_RULE_REQUIRED,
719 'fw_rule_removed' => $NEW_RULE_REMOVED,
723 'category' => 'Rijndael SPA',
724 'subcategory' => 'client+server',
725 'detail' => 'user mismatch (tcp/22 ssh)',
726 'err_msg' => "improper user accepted for access",
727 'function' => \&user_mismatch,
728 'function' => \&spa_cycle,
729 'cmdline' => $default_client_args,
730 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
731 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
732 "-d $default_digest_file -p $default_pid_file $intf_str",
733 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
734 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
738 'category' => 'Rijndael SPA',
739 'subcategory' => 'client+server',
740 'detail' => 'require src (tcp/22 ssh)',
741 'err_msg' => "fw rule not created",
742 'function' => \&spa_cycle,
743 'cmdline' => $default_client_args,
744 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
745 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
746 "-d $default_digest_file -p $default_pid_file $intf_str",
747 'fw_rule_created' => $NEW_RULE_REQUIRED,
748 'fw_rule_removed' => $NEW_RULE_REMOVED,
752 'category' => 'Rijndael SPA',
753 'subcategory' => 'client+server',
754 'detail' => 'mismatch require src (tcp/22 ssh)',
755 'err_msg' => "fw rule created",
756 'function' => \&spa_cycle,
757 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
758 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
759 "$local_key_file --verbose --verbose",
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 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
764 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
769 'category' => 'Rijndael SPA',
770 'subcategory' => 'client+server',
771 'detail' => 'IP filtering (tcp/22 ssh)',
772 'err_msg' => "did not filter $loopback_ip",
773 'function' => \&spa_cycle,
774 'cmdline' => $default_client_args,
775 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
776 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
777 "-d $default_digest_file -p $default_pid_file $intf_str",
778 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
779 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
783 'category' => 'Rijndael SPA',
784 'subcategory' => 'client+server',
785 'detail' => 'subnet filtering (tcp/22 ssh)',
786 'err_msg' => "did not filter $loopback_ip",
787 'function' => \&spa_cycle,
788 'cmdline' => $default_client_args,
789 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
790 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
791 "-d $default_digest_file -p $default_pid_file $intf_str",
792 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
793 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
797 'category' => 'Rijndael SPA',
798 'subcategory' => 'client+server',
799 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
800 'err_msg' => "did not filter $loopback_ip",
801 'function' => \&spa_cycle,
802 'cmdline' => $default_client_args,
803 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
804 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
805 "-d $default_digest_file -p $default_pid_file $intf_str",
806 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
807 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
811 'category' => 'Rijndael SPA',
812 'subcategory' => 'client+server',
813 'detail' => 'IP match (tcp/22 ssh)',
814 'err_msg' => "did not filter $loopback_ip",
815 'function' => \&spa_cycle,
816 'cmdline' => $default_client_args,
817 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
818 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
819 "-d $default_digest_file -p $default_pid_file $intf_str",
820 'fw_rule_created' => $NEW_RULE_REQUIRED,
821 'fw_rule_removed' => $NEW_RULE_REMOVED,
825 'category' => 'Rijndael SPA',
826 'subcategory' => 'client+server',
827 'detail' => 'subnet match (tcp/22 ssh)',
828 'err_msg' => "did not filter $loopback_ip",
829 'function' => \&spa_cycle,
830 'cmdline' => $default_client_args,
831 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
832 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
833 "-d $default_digest_file -p $default_pid_file $intf_str",
834 'fw_rule_created' => $NEW_RULE_REQUIRED,
835 'fw_rule_removed' => $NEW_RULE_REMOVED,
839 'category' => 'Rijndael SPA',
840 'subcategory' => 'client+server',
841 'detail' => 'multi IP/net match (tcp/22 ssh)',
842 'err_msg' => "did not filter $loopback_ip",
843 'function' => \&spa_cycle,
844 'cmdline' => $default_client_args,
845 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
846 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
847 "-d $default_digest_file -p $default_pid_file $intf_str",
848 'fw_rule_created' => $NEW_RULE_REQUIRED,
849 'fw_rule_removed' => $NEW_RULE_REMOVED,
853 'category' => 'Rijndael SPA',
854 'subcategory' => 'client+server',
855 'detail' => 'multi access stanzas (tcp/22 ssh)',
856 'err_msg' => "could not complete SPA cycle",
857 'function' => \&spa_cycle,
858 'cmdline' => $default_client_args,
859 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
860 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
861 "-d $default_digest_file -p $default_pid_file $intf_str",
862 'fw_rule_created' => $NEW_RULE_REQUIRED,
863 'fw_rule_removed' => $NEW_RULE_REMOVED,
867 'category' => 'Rijndael SPA',
868 'subcategory' => 'client+server',
869 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
870 'err_msg' => "could not complete SPA cycle",
871 'function' => \&spa_cycle,
872 'cmdline' => $default_client_args,
873 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
874 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
875 "-d $default_digest_file -p $default_pid_file $intf_str",
876 'fw_rule_created' => $NEW_RULE_REQUIRED,
877 'fw_rule_removed' => $NEW_RULE_REMOVED,
882 'category' => 'Rijndael SPA',
883 'subcategory' => 'client+server',
884 'detail' => "non-enabled NAT (tcp/22 ssh)",
885 'err_msg' => "SPA packet not filtered",
886 'function' => \&spa_cycle,
887 'cmdline' => "$default_client_args -N $internal_nat_host:22",
888 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
889 "$fwknopdCmd $default_server_conf_args $intf_str",
890 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
891 'server_conf' => $nat_conf,
892 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
896 'category' => 'Rijndael SPA',
897 'subcategory' => 'client+server',
898 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
899 'err_msg' => "could not complete NAT SPA cycle",
900 'function' => \&spa_cycle,
901 'cmdline' => "$default_client_args -N $internal_nat_host:22",
902 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
903 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
904 "-d $default_digest_file -p $default_pid_file $intf_str",
905 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
906 'fw_rule_created' => $NEW_RULE_REQUIRED,
907 'fw_rule_removed' => $NEW_RULE_REMOVED,
908 'server_conf' => $nat_conf,
913 'category' => 'Rijndael SPA',
914 'subcategory' => 'client+server',
915 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
916 'err_msg' => "could not complete NAT SPA cycle",
917 'function' => \&spa_cycle,
918 'cmdline' => $default_client_args,
919 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
920 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
921 "-d $default_digest_file -p $default_pid_file $intf_str",
922 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
923 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
924 'fw_rule_created' => $NEW_RULE_REQUIRED,
925 'fw_rule_removed' => $NEW_RULE_REMOVED,
926 'server_conf' => $nat_conf,
930 'category' => 'Rijndael SPA',
931 'subcategory' => 'client+server',
932 'detail' => 'ECB mode (tcp/22 ssh)',
933 'err_msg' => 'could not complete SPA cycle',
934 'function' => \&spa_cycle,
935 'cmdline' => "$default_client_args -M ecb",
936 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
937 "$fwknopdCmd -c $default_conf -a $ecb_mode_access_conf " .
938 "-d $default_digest_file -p $default_pid_file $intf_str",
939 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
940 'fw_rule_created' => $NEW_RULE_REQUIRED,
941 'fw_rule_removed' => $NEW_RULE_REMOVED,
945 'category' => 'Rijndael SPA',
946 'subcategory' => 'client+server',
947 'detail' => 'CFB mode (tcp/22 ssh)',
948 'err_msg' => 'could not complete SPA cycle',
949 'function' => \&spa_cycle,
950 'cmdline' => "$default_client_args -M cfb",
951 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
952 "$fwknopdCmd -c $default_conf -a $cfb_mode_access_conf " .
953 "-d $default_digest_file -p $default_pid_file $intf_str",
954 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
955 'fw_rule_created' => $NEW_RULE_REQUIRED,
956 'fw_rule_removed' => $NEW_RULE_REMOVED,
960 'category' => 'Rijndael SPA',
961 'subcategory' => 'client+server',
962 'detail' => 'CTR mode (tcp/22 ssh)',
963 'err_msg' => 'could not complete SPA cycle',
964 'function' => \&spa_cycle,
965 'cmdline' => "$default_client_args -M ctr",
966 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
967 "$fwknopdCmd -c $default_conf -a $ctr_mode_access_conf " .
968 "-d $default_digest_file -p $default_pid_file $intf_str",
969 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
970 'fw_rule_created' => $NEW_RULE_REQUIRED,
971 'fw_rule_removed' => $NEW_RULE_REMOVED,
975 'category' => 'Rijndael SPA',
976 'subcategory' => 'client+server',
977 'detail' => 'OFB mode (tcp/22 ssh)',
978 'err_msg' => 'could not complete SPA cycle',
979 'function' => \&spa_cycle,
980 'cmdline' => "$default_client_args -M ofb",
981 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
982 "$fwknopdCmd -c $default_conf -a $ofb_mode_access_conf " .
983 "-d $default_digest_file -p $default_pid_file $intf_str",
984 'server_negative_output_matches' => [qr/Decryption\sfailed/i],
985 'fw_rule_created' => $NEW_RULE_REQUIRED,
986 'fw_rule_removed' => $NEW_RULE_REMOVED,
991 'category' => 'Rijndael SPA',
992 'subcategory' => 'client+server',
993 'detail' => 'mode mismatch (tcp/22 ssh)',
994 'err_msg' => 'server accepted mismatch enc mode',
995 'function' => \&spa_cycle,
996 'cmdline' => "$default_client_args -M ecb",
997 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
998 "$fwknopdCmd -c $default_conf -a $default_access_conf " .
999 "-d $default_digest_file -p $default_pid_file $intf_str",
1000 'server_positive_output_matches' => [qr/Decryption\sfailed/i],
1001 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
1006 'category' => 'Rijndael SPA',
1007 'subcategory' => 'client+server',
1008 'detail' => 'complete cycle (tcp/23 telnet)',
1009 'err_msg' => 'could not complete SPA cycle',
1010 'function' => \&spa_cycle,
1011 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1012 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1013 "$local_key_file --verbose --verbose",
1014 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1015 "$fwknopdCmd $default_server_conf_args $intf_str",
1016 'fw_rule_created' => $NEW_RULE_REQUIRED,
1017 'fw_rule_removed' => $NEW_RULE_REMOVED,
1021 'category' => 'Rijndael SPA',
1022 'subcategory' => 'client+server',
1023 'detail' => 'complete cycle (tcp/9418 git)',
1024 'err_msg' => 'could not complete SPA cycle',
1025 'function' => \&spa_cycle,
1026 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1027 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1028 "$local_key_file --verbose --verbose",
1029 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1030 "$fwknopdCmd $default_server_conf_args $intf_str",
1031 'fw_rule_created' => $NEW_RULE_REQUIRED,
1032 'fw_rule_removed' => $NEW_RULE_REMOVED,
1036 'category' => 'Rijndael SPA',
1037 'subcategory' => 'client+server',
1038 'detail' => 'complete cycle (udp/53 dns)',
1039 'err_msg' => 'could not complete SPA cycle',
1040 'function' => \&spa_cycle,
1041 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1042 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1043 "$local_key_file --verbose --verbose",
1044 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1045 "$fwknopdCmd $default_server_conf_args $intf_str",
1046 'fw_rule_created' => $NEW_RULE_REQUIRED,
1047 'fw_rule_removed' => $NEW_RULE_REMOVED,
1051 'category' => 'Rijndael SPA',
1052 'subcategory' => 'client+server',
1053 'detail' => "-P bpf SPA over port $non_std_spa_port",
1054 'err_msg' => 'could not complete SPA cycle',
1055 'function' => \&spa_cycle,
1056 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
1057 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1058 "$fwknopdCmd $default_server_conf_args $intf_str " .
1059 qq|-P "udp port $non_std_spa_port"|,
1060 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
1061 'fw_rule_created' => $NEW_RULE_REQUIRED,
1062 'fw_rule_removed' => $NEW_RULE_REMOVED,
1067 'category' => 'Rijndael SPA',
1068 'subcategory' => 'client+server',
1069 'detail' => 'random SPA port (tcp/22 ssh)',
1070 'err_msg' => 'could not complete SPA cycle',
1071 'function' => \&spa_cycle,
1072 'cmdline' => "$default_client_args -r",
1073 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1074 "$fwknopdCmd $default_server_conf_args $intf_str " .
1076 'fw_rule_created' => $NEW_RULE_REQUIRED,
1077 'fw_rule_removed' => $NEW_RULE_REMOVED,
1082 'category' => 'Rijndael SPA',
1083 'subcategory' => 'client+server',
1084 'detail' => 'spoof username (tcp/22)',
1085 'err_msg' => 'could not spoof username',
1086 'function' => \&spoof_username,
1087 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1088 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1089 "$local_key_file --verbose --verbose",
1090 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1091 "$fwknopdCmd $default_server_conf_args $intf_str",
1096 'category' => 'Rijndael SPA',
1097 'subcategory' => 'client+server',
1098 'detail' => 'replay attack detection',
1099 'err_msg' => 'could not detect replay attack',
1100 'function' => \&replay_detection,
1101 'cmdline' => $default_client_args,
1102 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1103 "$fwknopdCmd $default_server_conf_args $intf_str",
1107 'category' => 'Rijndael SPA',
1108 'subcategory' => 'server',
1109 'detail' => 'digest cache structure',
1110 'err_msg' => 'improper digest cache structure',
1111 'function' => \&digest_cache_structure,
1116 'category' => 'Rijndael SPA',
1117 'subcategory' => 'client+server',
1118 'detail' => 'non-base64 altered SPA data',
1119 'err_msg' => 'allowed improper SPA data',
1120 'function' => \&altered_non_base64_spa_data,
1121 'cmdline' => $default_client_args,
1122 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1123 "$fwknopdCmd $default_server_conf_args $intf_str",
1127 'category' => 'Rijndael SPA',
1128 'subcategory' => 'client+server',
1129 'detail' => 'base64 altered SPA data',
1130 'err_msg' => 'allowed improper SPA data',
1131 'function' => \&altered_base64_spa_data,
1132 'cmdline' => $default_client_args,
1133 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1134 "$fwknopdCmd $default_server_conf_args $intf_str",
1138 'category' => 'Rijndael SPA',
1139 'subcategory' => 'client+server',
1140 'detail' => 'appended data to SPA pkt',
1141 'err_msg' => 'allowed improper SPA data',
1142 'function' => \&appended_spa_data,
1143 'cmdline' => $default_client_args,
1144 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1145 "$fwknopdCmd $default_server_conf_args $intf_str",
1149 'category' => 'Rijndael SPA',
1150 'subcategory' => 'client+server',
1151 'detail' => 'prepended data to SPA pkt',
1152 'err_msg' => 'allowed improper SPA data',
1153 'function' => \&prepended_spa_data,
1154 'cmdline' => $default_client_args,
1155 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1156 "$fwknopdCmd $default_server_conf_args $intf_str",
1161 'category' => 'GnuPG (GPG) SPA',
1162 'subcategory' => 'client+server',
1163 'detail' => 'complete cycle (tcp/22 ssh)',
1164 'err_msg' => 'could not complete SPA cycle',
1165 'function' => \&spa_cycle,
1166 'cmdline' => $default_client_gpg_args,
1167 'fwknopd_cmdline' => $default_server_gpg_args,
1168 'fw_rule_created' => $NEW_RULE_REQUIRED,
1169 'fw_rule_removed' => $NEW_RULE_REMOVED,
1173 'category' => 'GnuPG (GPG) SPA',
1174 'subcategory' => 'client+server',
1175 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1176 'err_msg' => 'could not complete SPA cycle',
1177 'function' => \&spa_cycle,
1178 'cmdline' => $default_client_gpg_args,
1179 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1180 "$valgrind_str $fwknopdCmd -c $default_conf " .
1181 "-a $multi_gpg_access_conf $intf_str " .
1182 "-d $default_digest_file -p $default_pid_file",
1183 'fw_rule_created' => $NEW_RULE_REQUIRED,
1184 'fw_rule_removed' => $NEW_RULE_REMOVED,
1189 'category' => 'GnuPG (GPG) SPA',
1190 'subcategory' => 'client+server',
1191 'detail' => 'complete cycle (tcp/23 telnet)',
1192 'err_msg' => 'could not complete SPA cycle',
1193 'function' => \&spa_cycle,
1194 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1195 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1196 "$local_key_file --verbose --verbose " .
1197 "--gpg-recipient-key $gpg_server_key " .
1198 "--gpg-signer-key $gpg_client_key " .
1199 "--gpg-home-dir $gpg_client_home_dir",
1200 'fwknopd_cmdline' => $default_server_gpg_args,
1201 'fw_rule_created' => $NEW_RULE_REQUIRED,
1202 'fw_rule_removed' => $NEW_RULE_REMOVED,
1206 'category' => 'GnuPG (GPG) SPA',
1207 'subcategory' => 'client+server',
1208 'detail' => 'complete cycle (tcp/9418 git)',
1209 'err_msg' => 'could not complete SPA cycle',
1210 'function' => \&spa_cycle,
1211 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1212 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1213 "$local_key_file --verbose --verbose " .
1214 "--gpg-recipient-key $gpg_server_key " .
1215 "--gpg-signer-key $gpg_client_key " .
1216 "--gpg-home-dir $gpg_client_home_dir",
1217 'fwknopd_cmdline' => $default_server_gpg_args,
1218 'fw_rule_created' => $NEW_RULE_REQUIRED,
1219 'fw_rule_removed' => $NEW_RULE_REMOVED,
1223 'category' => 'GnuPG (GPG) SPA',
1224 'subcategory' => 'client+server',
1225 'detail' => 'complete cycle (udp/53 dns)',
1226 'err_msg' => 'could not complete SPA cycle',
1227 'function' => \&spa_cycle,
1228 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1229 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1230 "$local_key_file --verbose --verbose " .
1231 "--gpg-recipient-key $gpg_server_key " .
1232 "--gpg-signer-key $gpg_client_key " .
1233 "--gpg-home-dir $gpg_client_home_dir",
1234 'fwknopd_cmdline' => $default_server_gpg_args,
1235 'fw_rule_created' => $NEW_RULE_REQUIRED,
1236 'fw_rule_removed' => $NEW_RULE_REMOVED,
1241 'category' => 'GnuPG (GPG) SPA',
1242 'subcategory' => 'client+server',
1243 'detail' => 'replay attack detection',
1244 'err_msg' => 'could not detect replay attack',
1245 'function' => \&replay_detection,
1246 'cmdline' => $default_client_gpg_args,
1247 'fwknopd_cmdline' => $default_server_gpg_args,
1252 'category' => 'GnuPG (GPG) SPA',
1253 'subcategory' => 'client+server',
1254 'detail' => 'non-base64 altered SPA data',
1255 'err_msg' => 'allowed improper SPA data',
1256 'function' => \&altered_non_base64_spa_data,
1257 'cmdline' => $default_client_gpg_args,
1258 'fwknopd_cmdline' => $default_server_gpg_args,
1262 'category' => 'GnuPG (GPG) SPA',
1263 'subcategory' => 'client+server',
1264 'detail' => 'base64 altered SPA data',
1265 'err_msg' => 'allowed improper SPA data',
1266 'function' => \&altered_base64_spa_data,
1267 'cmdline' => $default_client_gpg_args,
1268 'fwknopd_cmdline' => $default_server_gpg_args,
1272 'category' => 'GnuPG (GPG) SPA',
1273 'subcategory' => 'client+server',
1274 'detail' => 'appended data to SPA pkt',
1275 'err_msg' => 'allowed improper SPA data',
1276 'function' => \&appended_spa_data,
1277 'cmdline' => $default_client_gpg_args,
1278 'fwknopd_cmdline' => $default_server_gpg_args,
1282 'category' => 'GnuPG (GPG) SPA',
1283 'subcategory' => 'client+server',
1284 'detail' => 'prepended data to SPA pkt',
1285 'err_msg' => 'allowed improper SPA data',
1286 'function' => \&prepended_spa_data,
1287 'cmdline' => $default_client_gpg_args,
1288 'fwknopd_cmdline' => $default_server_gpg_args,
1292 'category' => 'GnuPG (GPG) SPA',
1293 'subcategory' => 'client+server',
1294 'detail' => 'spoof username (tcp/22 ssh)',
1295 'err_msg' => 'could not spoof username',
1296 'function' => \&spoof_username,
1297 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1298 'fwknopd_cmdline' => $default_server_gpg_args,
1303 'category' => 'GnuPG (GPG) SPA',
1304 'subcategory' => 'server',
1305 'detail' => 'digest cache structure',
1306 'err_msg' => 'improper digest cache structure',
1307 'function' => \&digest_cache_structure,
1313 'category' => $REQUIRED,
1314 'subcategory' => $OPTIONAL,
1315 'detail' => $REQUIRED,
1316 'function' => $REQUIRED,
1317 'binary' => $OPTIONAL,
1318 'cmdline' => $OPTIONAL,
1319 'fwknopd_cmdline' => $OPTIONAL,
1320 'fatal' => $OPTIONAL,
1321 'exec_err' => $OPTIONAL,
1322 'fw_rule_created' => $OPTIONAL,
1323 'fw_rule_removed' => $OPTIONAL,
1324 'server_conf' => $OPTIONAL,
1325 'positive_output_matches' => $OPTIONAL,
1326 'negative_output_matches' => $OPTIONAL,
1327 'server_positive_output_matches' => $OPTIONAL,
1328 'server_negative_output_matches' => $OPTIONAL,
1332 &diff_test_results();
1336 ### make sure everything looks as expected before continuing
1339 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1340 " args: @args_cp\n\n"
1343 ### save the results from any previous test suite run
1344 ### so that we can potentially compare them with --diff
1345 if ($saved_last_results) {
1346 &logr(" Saved results from previous run " .
1347 "to: ${output_dir}.last/\n\n");
1350 ### main loop through all of the tests
1351 for my $test_hr (@tests) {
1352 &run_test($test_hr);
1355 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1357 copy $logfile, "$output_dir/$logfile" or die $!;
1361 #===================== end main =======================
1364 my $test_hr = shift;
1366 my $msg = "[$test_hr->{'category'}]";
1367 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1368 $msg .= " $test_hr->{'detail'}";
1370 return unless &process_include_exclude($msg);
1380 $current_test_file = "$output_dir/$executed.test";
1381 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1383 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1384 $test_hr->{'msg'} = $msg;
1385 if (&{$test_hr->{'function'}}($test_hr)) {
1386 &logr("pass ($executed)\n");
1389 &logr("fail ($executed)\n");
1392 if ($test_hr->{'fatal'} eq $YES) {
1393 die "[*] required test failed, exiting.";
1400 sub process_include_exclude() {
1403 ### inclusions/exclusions
1404 if (@tests_to_include) {
1406 for my $test (@tests_to_include) {
1407 if ($msg =~ /$test/) {
1412 return 0 unless $found;
1414 if (@tests_to_exclude) {
1416 for my $test (@tests_to_exclude) {
1417 if ($msg =~ /$test/) {
1427 sub diff_test_results() {
1428 die "[*] Need results from a previous run before running --diff"
1429 unless -d "${output_dir}.last";
1430 die "[*] Current results set does not exist." unless -d $output_dir;
1432 my %current_tests = ();
1433 my %previous_tests = ();
1435 ### Only diff results for matching tests (parse the logfile to see which
1436 ### test numbers match across the two test cycles).
1437 &build_results_hash(\%current_tests, $output_dir);
1438 &build_results_hash(\%previous_tests, "${output_dir}.last");
1440 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1441 keys %current_tests) {
1442 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1443 my $current_num = $current_tests{$test_msg}{'num'};
1444 if (defined $previous_tests{$test_msg}) {
1445 print "[+] Checking: $test_msg\n";
1446 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1447 my $previous_num = $previous_tests{$test_msg}{'num'};
1448 if ($current_result ne $previous_result) {
1449 print " DIFF: **$current_result** $test_msg\n";
1452 &diff_results($previous_num, $current_num);
1460 sub diff_results() {
1461 my ($previous_num, $current_num) = @_;
1463 ### edit out any valgrind "==354==" prefixes
1464 my $valgrind_search_re = qr/^==\d+==\s/;
1466 ### remove CMD timestamps
1467 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1469 for my $file ("${output_dir}.last/${previous_num}.test",
1470 "${output_dir}.last/${previous_num}_fwknopd.test",
1471 "${output_dir}/${current_num}.test",
1472 "${output_dir}/${current_num}_fwknopd.test",
1474 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1475 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1478 if (-e "${output_dir}.last/${previous_num}.test"
1479 and -e "${output_dir}/${current_num}.test") {
1480 system "diff -u ${output_dir}.last/${previous_num}.test " .
1481 "${output_dir}/${current_num}.test";
1484 if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1485 and -e "${output_dir}/${current_num}_fwknopd.test") {
1486 system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1487 "${output_dir}/${current_num}_fwknopd.test";
1493 sub build_results_hash() {
1494 my ($hr, $dir) = @_;
1496 open F, "< $dir/$logfile" or die $!;
1498 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1499 $hr->{$1}{'pass_fail'} = $2;
1500 $hr->{$1}{'num'} = $3;
1506 sub compile_warnings() {
1508 ### 'make clean' as root
1509 return 0 unless &run_cmd('make -C .. clean',
1510 $cmd_out_tmp, $current_test_file);
1513 my $username = getpwuid((stat($configure_path))[4]);
1514 die "[*] Could not determine $configure_path owner"
1517 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1518 $cmd_out_tmp, $current_test_file);
1522 return 0 unless &run_cmd('make -C ..',
1523 $cmd_out_tmp, $current_test_file);
1527 ### look for compilation warnings - something like:
1528 ### warning: ‘test’ is used uninitialized in this function
1529 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/], $current_test_file);
1531 ### the new binaries should exist
1532 unless (-e $fwknopCmd and -x $fwknopCmd) {
1533 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1534 $current_test_file);
1536 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1537 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1538 $current_test_file);
1544 sub binary_exists() {
1545 my $test_hr = shift;
1546 return 0 unless $test_hr->{'binary'};
1548 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1549 ### libfko.so link on OpenBSD)
1551 if ($test_hr->{'binary'} =~ /libfko/) {
1552 unless (-e $test_hr->{'binary'}) {
1553 for my $file (glob("$lib_dir/libfko.so*")) {
1554 if (-e $file and -x $file) {
1555 $test_hr->{'binary'} = $file;
1556 $libfko_bin = $file;
1563 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1567 sub expected_code_version() {
1568 my $test_hr = shift;
1570 unless (-e '../VERSION') {
1571 &write_test_file("[-] ../VERSION file does not exist.\n",
1572 $current_test_file);
1576 open F, '< ../VERSION' or die $!;
1579 if ($line =~ /(\d.*\d)/) {
1581 return 0 unless &run_cmd($test_hr->{'cmdline'},
1582 $cmd_out_tmp, $current_test_file);
1583 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1588 sub client_send_spa_packet() {
1589 my $test_hr = shift;
1591 &write_key('fwknoptest', $local_key_file);
1593 return 0 unless &run_cmd($test_hr->{'cmdline'},
1594 $cmd_out_tmp, $current_test_file);
1595 return 0 unless &file_find_regex([qr/final\spacked/i],
1596 $current_test_file);
1602 my $test_hr = shift;
1604 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1605 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1607 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1608 $rv = 0 unless $fw_rule_created;
1609 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1610 $rv = 0 if $fw_rule_created;
1613 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1614 $rv = 0 unless $fw_rule_removed;
1615 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1616 $rv = 0 if $fw_rule_removed;
1619 if ($test_hr->{'server_positive_output_matches'}) {
1620 $rv = 0 unless &file_find_regex(
1621 $test_hr->{'server_positive_output_matches'},
1625 if ($test_hr->{'server_negative_output_matches'}) {
1626 $rv = 0 if &file_find_regex(
1627 $test_hr->{'server_negative_output_matches'},
1634 sub spoof_username() {
1635 my $test_hr = shift;
1637 my $rv = &spa_cycle($test_hr);
1639 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1640 $current_test_file)) {
1644 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1645 $server_test_file)) {
1652 sub replay_detection() {
1653 my $test_hr = shift;
1655 ### do a complete SPA cycle and then parse the SPA packet out of the
1656 ### current test file and re-send
1658 return 0 unless &spa_cycle($test_hr);
1660 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1663 &write_test_file("[-] could not get SPA packet " .
1664 "from file: $current_test_file\n",
1665 $current_test_file);
1672 'port' => $default_spa_port,
1673 'dst_ip' => $loopback_ip,
1678 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1679 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1681 $rv = 0 unless $server_was_stopped;
1683 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1684 $server_test_file)) {
1691 sub digest_cache_structure() {
1692 my $test_hr = shift;
1695 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1697 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1699 ### the format should be:
1700 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1701 open F, "< $default_digest_file" or
1702 die "[*] could not open $default_digest_file: $!";
1706 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1707 &write_test_file("[-] invalid digest.cache line: $_",
1708 $current_test_file);
1714 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1715 &write_test_file("[+] DBM digest file format, " .
1716 "assuming this is valid.\n", $current_test_file);
1718 ### don't know what kind of file the digest.cache is
1719 &write_test_file("[-] unrecognized file type for " .
1720 "$default_digest_file.\n", $current_test_file);
1725 &write_test_file("[+] valid digest.cache structure.\n",
1726 $current_test_file);
1732 sub server_bpf_ignore_packet() {
1733 my $test_hr = shift;
1736 my $server_was_stopped = 0;
1737 my $fw_rule_created = 0;
1738 my $fw_rule_removed = 0;
1740 unless (&client_send_spa_packet($test_hr)) {
1741 &write_test_file("[-] fwknop client execution error.\n",
1742 $current_test_file);
1746 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1749 &write_test_file("[-] could not get SPA packet " .
1750 "from file: $current_test_file\n", $current_test_file);
1757 'port' => $default_spa_port,
1758 'dst_ip' => $loopback_ip,
1763 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1764 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1766 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1767 $server_test_file)) {
1774 sub altered_non_base64_spa_data() {
1775 my $test_hr = shift;
1778 my $server_was_stopped = 0;
1779 my $fw_rule_created = 0;
1780 my $fw_rule_removed = 0;
1782 unless (&client_send_spa_packet($test_hr)) {
1783 &write_test_file("[-] fwknop client execution error.\n",
1784 $current_test_file);
1788 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1791 &write_test_file("[-] could not get SPA packet " .
1792 "from file: $current_test_file\n", $current_test_file);
1796 ### alter one byte (change to a ":")
1797 $spa_pkt =~ s|^(.{3}).|$1:|;
1802 'port' => $default_spa_port,
1803 'dst_ip' => $loopback_ip,
1808 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1809 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1811 $rv = 0 unless $server_was_stopped;
1816 sub altered_base64_spa_data() {
1817 my $test_hr = shift;
1820 my $server_was_stopped = 0;
1821 my $fw_rule_created = 0;
1822 my $fw_rule_removed = 0;
1824 unless (&client_send_spa_packet($test_hr)) {
1825 &write_test_file("[-] fwknop client execution error.\n",
1826 $current_test_file);
1830 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1833 &write_test_file("[-] could not get SPA packet " .
1834 "from file: $current_test_file\n", $current_test_file);
1838 $spa_pkt =~ s|^(.{3}).|AAAA|;
1843 'port' => $default_spa_port,
1844 'dst_ip' => $loopback_ip,
1849 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1850 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1852 $rv = 0 unless $server_was_stopped;
1854 if ($fw_rule_created) {
1855 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1858 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1861 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1862 $server_test_file)) {
1869 sub appended_spa_data() {
1870 my $test_hr = shift;
1873 my $server_was_stopped = 0;
1874 my $fw_rule_created = 0;
1875 my $fw_rule_removed = 0;
1877 unless (&client_send_spa_packet($test_hr)) {
1878 &write_test_file("[-] fwknop client execution error.\n",
1879 $current_test_file);
1883 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1886 &write_test_file("[-] could not get SPA packet " .
1887 "from file: $current_test_file\n", $current_test_file);
1896 'port' => $default_spa_port,
1897 'dst_ip' => $loopback_ip,
1902 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1903 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1905 $rv = 0 unless $server_was_stopped;
1907 if ($fw_rule_created) {
1908 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1911 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1914 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1915 $server_test_file)) {
1922 sub prepended_spa_data() {
1923 my $test_hr = shift;
1926 my $server_was_stopped = 0;
1927 my $fw_rule_created = 0;
1928 my $fw_rule_removed = 0;
1930 unless (&client_send_spa_packet($test_hr)) {
1931 &write_test_file("[-] fwknop client execution error.\n",
1932 $current_test_file);
1936 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1939 &write_test_file("[-] could not get SPA packet " .
1940 "from file: $current_test_file\n", $current_test_file);
1944 $spa_pkt = 'AAAA' . $spa_pkt;
1949 'port' => $default_spa_port,
1950 'dst_ip' => $loopback_ip,
1955 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1956 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1958 $rv = 0 unless $server_was_stopped;
1960 if ($fw_rule_created) {
1961 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1964 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1967 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1968 $server_test_file)) {
1975 sub server_start() {
1976 my $test_hr = shift;
1978 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1979 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1981 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1982 $server_test_file)) {
1986 $rv = 0 unless $server_was_stopped;
1992 my $test_hr = shift;
1994 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1995 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1997 $rv = 0 unless $server_was_stopped;
2002 sub server_packet_limit() {
2003 my $test_hr = shift;
2008 'port' => $default_spa_port,
2009 'dst_ip' => $loopback_ip,
2014 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2015 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2017 if (&is_fwknopd_running()) {
2022 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
2023 $server_test_file)) {
2027 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
2028 $server_test_file)) {
2035 sub server_ignore_small_packets() {
2036 my $test_hr = shift;
2041 'port' => $default_spa_port,
2042 'dst_ip' => $loopback_ip,
2043 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
2047 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
2048 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
2052 if (&is_fwknopd_running()) {
2060 sub client_server_interaction() {
2061 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
2064 my $server_was_stopped = 1;
2065 my $fw_rule_created = 1;
2066 my $fw_rule_removed = 0;
2068 ### start fwknopd to monitor for the SPA packet over the loopback interface
2069 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
2071 ### give fwknopd a chance to parse its config and start sniffing
2072 ### on the loopback interface
2073 if ($use_valgrind) {
2079 ### send the SPA packet(s) to the server either manually using IO::Socket or
2080 ### with the fwknopd client
2081 if ($spa_client_flag == $USE_CLIENT) {
2082 unless (&client_send_spa_packet($test_hr)) {
2083 &write_test_file("[-] fwknop client execution error.\n",
2084 $current_test_file);
2088 &send_packets($pkts_hr);
2091 ### check to see if the SPA packet resulted in a new fw access rule
2093 while (not &is_fw_rule_active($test_hr)) {
2094 &write_test_file("[-] new fw rule does not exist.\n",
2095 $current_test_file);
2101 $fw_rule_created = 0;
2102 $fw_rule_removed = 0;
2105 &time_for_valgrind() if $use_valgrind;
2107 if ($fw_rule_created) {
2108 sleep 3; ### allow time for rule time out.
2109 if (&is_fw_rule_active($test_hr)) {
2110 &write_test_file("[-] new fw rule not timed out.\n",
2111 $current_test_file);
2114 &write_test_file("[+] new fw rule timed out.\n",
2115 $current_test_file);
2116 $fw_rule_removed = 1;
2120 if (&is_fwknopd_running()) {
2122 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2123 $server_test_file)) {
2124 $server_was_stopped = 0;
2127 &write_test_file("[-] server is not running.\n",
2128 $current_test_file);
2129 $server_was_stopped = 0;
2132 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2135 sub get_spa_packet_from_file() {
2140 my $found_trigger_line = 0;
2141 open F, "< $file" or die "[*] Could not open file $file: $!";
2143 if (/final\spacked/i) {
2144 $found_trigger_line = 1;
2147 next unless $found_trigger_line;
2149 ### the next line with non whitespace is the SPA packet
2160 sub send_packets() {
2161 my $pkts_ar = shift;
2163 open F, ">> $current_test_file" or die $!;
2164 print F "[+] send_packets(): Sending the following packets...\n";
2165 print F Dumper $pkts_ar;
2168 for my $pkt_hr (@$pkts_ar) {
2169 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2170 my $socket = IO::Socket::INET->new(
2171 PeerAddr => $pkt_hr->{'dst_ip'},
2172 PeerPort => $pkt_hr->{'port'},
2173 Proto => $pkt_hr->{'proto'},
2175 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2176 "socket to $pkt_hr->{'dst_ip'}: $!";
2178 $socket->send($pkt_hr->{'data'});
2181 } elsif ($pkt_hr->{'proto'} eq 'http') {
2183 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2187 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2192 sub generic_exec() {
2193 my $test_hr = shift;
2197 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2198 $cmd_out_tmp, $current_test_file);
2200 if ($test_hr->{'exec_err'} eq $YES) {
2201 $rv = 0 if $exec_rv;
2203 $rv = 0 unless $exec_rv;
2206 if ($test_hr->{'positive_output_matches'}) {
2207 $rv = 0 unless &file_find_regex(
2208 $test_hr->{'positive_output_matches'},
2209 $current_test_file);
2212 if ($test_hr->{'negative_output_matches'}) {
2213 $rv = 0 if &file_find_regex(
2214 $test_hr->{'negative_output_matches'},
2215 $current_test_file);
2223 my $test_hr = shift;
2224 return 0 unless $test_hr->{'binary'};
2225 &run_cmd("./hardening-check $test_hr->{'binary'}",
2226 $cmd_out_tmp, $current_test_file);
2227 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2228 $current_test_file);
2232 ### check for stack protection
2233 sub stack_protected_binary() {
2234 my $test_hr = shift;
2235 return 0 unless $test_hr->{'binary'};
2236 &run_cmd("./hardening-check $test_hr->{'binary'}",
2237 $cmd_out_tmp, $current_test_file);
2238 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2239 $current_test_file);
2243 ### check for fortified source functions
2244 sub fortify_source_functions() {
2245 my $test_hr = shift;
2246 return 0 unless $test_hr->{'binary'};
2247 &run_cmd("./hardening-check $test_hr->{'binary'}",
2248 $cmd_out_tmp, $current_test_file);
2249 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2250 $current_test_file);
2254 ### check for read-only relocations
2255 sub read_only_relocations() {
2256 my $test_hr = shift;
2257 return 0 unless $test_hr->{'binary'};
2258 &run_cmd("./hardening-check $test_hr->{'binary'}",
2259 $cmd_out_tmp, $current_test_file);
2260 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2261 $current_test_file);
2265 ### check for immediate binding
2266 sub immediate_binding() {
2267 my $test_hr = shift;
2268 return 0 unless $test_hr->{'binary'};
2269 &run_cmd("./hardening-check $test_hr->{'binary'}",
2270 $cmd_out_tmp, $current_test_file);
2271 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2272 $current_test_file);
2278 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2279 "$default_server_conf_args --fw-list-all",
2280 $cmd_out_tmp, $current_test_file);
2288 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2289 'if [ `which iptables` ]; then iptables -V; fi',
2290 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2291 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2292 'if [ `which gpg` ]; then gpg --version; fi',
2293 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2297 'ls -l /usr/lib/*pcap*',
2298 'ls -l /usr/local/lib/*pcap*',
2299 'ls -l /usr/lib/*fko*',
2300 'ls -l /usr/local/lib/*fko*',
2302 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2304 if ($cmd =~ /^ldd/) {
2305 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2309 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2310 ### to enable gpg tests
2311 unless ($have_gpgme == 3) {
2312 push @tests_to_exclude, "GPG";
2318 sub time_for_valgrind() {
2320 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2321 "grep valgrind |grep -v perl | grep -v grep",
2322 $cmd_out_tmp, $current_test_file)) {
2330 sub anonymize_results() {
2332 die "[*] $output_dir does not exist" unless -d $output_dir;
2333 die "[*] $logfile does not exist, has $0 been executed?"
2336 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2339 ### remove non-loopback IP addresses
2340 my $search_re = qr/\b127\.0\.0\.1\b/;
2341 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2342 $search_re = qr/\b127\.0\.0\.2\b/;
2343 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2344 $search_re = qr/\b0\.0\.0\.0\b/;
2345 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2346 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2347 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2348 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2349 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2350 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2352 ### remove hostname from any uname output
2353 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2354 system "perl -p -i -e 'undef \$/; s|$search_re" .
2355 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2357 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2358 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2361 system "tar cvfz $tarfile $logfile $output_dir";
2362 print "[+] Anonymized test results file: $tarfile\n";
2371 my $test_hr = shift;
2373 open F, "> $default_pid_file" or die $!;
2377 &server_start($test_hr);
2379 open F, "< $default_pid_file" or die $!;
2391 sub start_fwknopd() {
2392 my $test_hr = shift;
2394 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2397 die "[*] Could not fork: $!" unless defined $pid;
2401 ### we are the child, so start fwknopd
2402 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2403 $server_cmd_tmp, $server_test_file);
2409 my ($key, $file) = @_;
2411 open K, "> $file" or die "[*] Could not open $file: $!";
2412 print K "$loopback_ip: $key\n";
2413 print K "localhost: $key\n";
2414 print K "some.host.through.proxy.com: $key\n";
2420 open C, ">> $current_test_file"
2421 or die "[*] Could not open $current_test_file: $!";
2422 print C "\n" . localtime() . " [+] PID dump:\n";
2424 &run_cmd("ps auxww | grep knop |grep -v grep",
2425 $cmd_out_tmp, $current_test_file);
2430 my ($cmd, $cmd_out, $file) = @_;
2434 or die "[*] Could not open $file: $!";
2435 print F localtime() . " CMD: $cmd\n";
2439 or die "[*] Could not open $file: $!";
2440 print F localtime() . " CMD: $cmd\n";
2444 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2446 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2447 my @cmd_lines = <C>;
2450 open F, ">> $file" or die "[*] Could not open $file: $!";
2451 print F $_ for @cmd_lines;
2464 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2473 $|++; ### turn off buffering
2475 $< == 0 && $> == 0 or
2476 die "[*] $0: You must be root (or equivalent ",
2477 "UID 0 account) to effectively test fwknop";
2479 ### validate test hashes
2481 for my $test_hr (@tests) {
2482 for my $key (keys %test_keys) {
2483 if ($test_keys{$key} == $REQUIRED) {
2484 die "[*] Missing '$key' element in hash: $hash_num"
2485 unless defined $test_hr->{$key};
2487 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2493 if ($use_valgrind) {
2494 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2495 unless -e $valgrindCmd and -x $valgrindCmd;
2498 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2499 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2501 for my $file ($configure_path,
2504 $default_access_conf,
2505 $no_source_match_access_conf,
2506 $ip_source_match_access_conf,
2507 $subnet_source_match_access_conf,
2508 $no_subnet_source_match_access_conf,
2509 $no_multi_source_match_access_conf,
2510 $multi_source_match_access_conf,
2511 $open_ports_access_conf,
2512 $mismatch_open_ports_access_conf,
2513 $require_user_access_conf,
2514 $mismatch_user_access_conf,
2515 $require_src_access_conf,
2516 $multi_gpg_access_conf,
2517 $multi_stanzas_access_conf,
2518 $expired_access_conf,
2519 $expired_epoch_access_conf,
2520 $future_expired_access_conf,
2521 $invalid_expire_access_conf,
2522 $force_nat_access_conf,
2524 die "[*] $file does not exist" unless -e $file;
2527 if (-d $output_dir) {
2528 if (-d "${output_dir}.last") {
2529 rmtree "${output_dir}.last"
2530 or die "[*] rmtree ${output_dir}.last $!";
2532 mkdir "${output_dir}.last"
2533 or die "[*] ${output_dir}.last: $!";
2534 for my $file (glob("$output_dir/*.test")) {
2535 if ($file =~ m|.*/(.*)|) {
2536 copy $file, "${output_dir}.last/$1" or die $!;
2539 if (-e "$output_dir/init") {
2540 copy "$output_dir/init", "${output_dir}.last/init";
2543 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2545 $saved_last_results = 1;
2547 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2549 unless (-d $run_dir) {
2550 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2553 for my $file (glob("$output_dir/*.test")) {
2554 unlink $file or die "[*] Could not unlink($file)";
2556 if (-e "$output_dir/init") {
2557 unlink "$output_dir/init" or die $!;
2561 unlink $logfile or die $!;
2564 if ($test_include) {
2565 @tests_to_include = split /\s*,\s*/, $test_include;
2567 if ($test_exclude) {
2568 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2571 ### make sure no fwknopd instance is currently running
2572 die "[*] Please stop the running fwknopd instance."
2573 if &is_fwknopd_running();
2575 unless ($enable_recompilation_warnings_check) {
2576 push @tests_to_exclude, 'recompilation';
2579 $sudo_path = &find_command('sudo');
2581 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2582 ### disable compilation checks
2583 push @tests_to_exclude, 'recompilation';
2586 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2589 $platform = 'linux';
2595 unless ($platform eq 'linux') {
2596 push @tests_to_exclude, 'NAT';
2602 sub identify_loopback_intf() {
2603 return if $loopback_intf;
2607 ### lo Link encap:Local Loopback
2608 ### inet addr:127.0.0.1 Mask:255.0.0.0
2609 ### inet6 addr: ::1/128 Scope:Host
2610 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2611 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2612 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2613 ### collisions:0 txqueuelen:0
2614 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2618 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2619 ### options=3<RXCSUM,TXCSUM>
2620 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2621 ### inet6 ::1 prefixlen 128
2622 ### inet 127.0.0.1 netmask 0xff000000
2623 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2626 my $found_loopback_intf = 0;
2628 my $cmd = 'ifconfig -a';
2629 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2631 if (/^(\S+?):?\s+.*loopback/i) {
2635 if (/^\S/ and $intf and not $found_loopback_intf) {
2636 ### should not happen
2639 if ($intf and /\b127\.0\.0\.1\b/) {
2640 $found_loopback_intf = 1;
2646 die "[*] could not determine loopback interface, use --loopback <name>"
2647 unless $found_loopback_intf;
2649 $loopback_intf = $intf;
2654 sub is_fw_rule_active() {
2655 my $test_hr = shift;
2657 my $conf_args = $default_server_conf_args;
2659 if ($test_hr->{'server_conf'}) {
2660 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2661 "-d $default_digest_file -p $default_pid_file";
2664 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2665 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2666 $cmd_out_tmp, $current_test_file);
2670 sub is_fwknopd_running() {
2672 sleep 2 if $use_valgrind;
2674 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2675 "--status", $cmd_out_tmp, $current_test_file);
2677 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2682 sub stop_fwknopd() {
2684 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2685 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2687 if ($use_valgrind) {
2688 &time_for_valgrind();
2696 sub file_find_regex() {
2697 my ($re_ar, $file) = @_;
2700 my @write_lines = ();
2702 open F, "< $file" or die "[*] Could not open $file: $!";
2705 next LINE if $line =~ /file_file_regex\(\)/;
2706 for my $re (@$re_ar) {
2708 push @write_lines, "[.] file_find_regex() " .
2709 "Matched '$re' with line: $line";
2718 for my $line (@write_lines) {
2719 &write_test_file($line, $file);
2722 &write_test_file("[.] find_find_regex() Did not " .
2723 "match any regex in: '@$re_ar'\n", $file);
2729 sub find_command() {
2733 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2735 if (m|^(/.*$cmd)$|) {
2744 sub write_test_file() {
2745 my ($msg, $file) = @_;
2749 or die "[*] Could not open $file: $!";
2754 or die "[*] Could not open $file: $!";
2764 open F, ">> $logfile" or die $!;