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 $gpg_access_conf = "$conf_dir/gpg_access.conf";
31 my $default_digest_file = "$run_dir/digest.cache";
32 my $default_pid_file = "$run_dir/fwknopd.pid";
33 my $open_ports_access_conf = "$conf_dir/open_ports_access.conf";
34 my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf";
35 my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf";
36 my $multi_stanzas_with_broken_keys_conf = "$conf_dir/multi_stanzas_with_broken_keys.conf";
37 my $mismatch_open_ports_access_conf = "$conf_dir/mismatch_open_ports_access.conf";
38 my $require_user_access_conf = "$conf_dir/require_user_access.conf";
39 my $mismatch_user_access_conf = "$conf_dir/mismatch_user_access.conf";
40 my $require_src_access_conf = "$conf_dir/require_src_access.conf";
41 my $no_source_match_access_conf = "$conf_dir/no_source_match_access.conf";
42 my $no_subnet_source_match_access_conf = "$conf_dir/no_subnet_source_match_access.conf";
43 my $no_multi_source_match_access_conf = "$conf_dir/no_multi_source_match_access.conf";
44 my $multi_source_match_access_conf = "$conf_dir/multi_source_match_access.conf";
45 my $ip_source_match_access_conf = "$conf_dir/ip_source_match_access.conf";
46 my $subnet_source_match_access_conf = "$conf_dir/subnet_source_match_access.conf";
48 my $fwknopCmd = '../client/.libs/fwknop';
49 my $fwknopdCmd = '../server/.libs/fwknopd';
50 my $libfko_bin = "$lib_dir/libfko.so"; ### this is usually a link
51 my $valgrindCmd = '/usr/bin/valgrind';
53 my $gpg_server_key = '361BBAD4';
54 my $gpg_client_key = '6A3FAD56';
56 my $loopback_ip = '127.0.0.1';
57 my $fake_ip = '127.0.0.2';
58 my $internal_nat_host = '192.168.1.2';
59 my $force_nat_host = '192.168.1.123';
60 my $default_spa_port = 62201;
61 my $non_std_spa_port = 12345;
63 my $spoof_user = 'testuser';
64 #================== end config ===================
69 my $test_include = '';
70 my @tests_to_include = ();
71 my $test_exclude = '';
72 my @tests_to_exclude = ();
74 my $loopback_intf = '';
75 my $anonymize_results = 0;
76 my $current_test_file = "$output_dir/init";
77 my $tarfile = 'test_fwknop.tar.gz';
78 my $server_test_file = '';
80 my $valgrind_str = '';
81 my $saved_last_results = 0;
83 my $enable_recompilation_warnings_check = 0;
90 my $USE_PREDEF_PKTS = 1;
94 my $NEW_RULE_REQUIRED = 1;
95 my $REQUIRE_NO_NEW_RULE = 2;
96 my $NEW_RULE_REMOVED = 1;
97 my $REQUIRE_NO_NEW_REMOVED = 2;
99 my $ip_re = qr|(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}|; ### IPv4
103 exit 1 unless GetOptions(
104 'Anonymize-results' => \$anonymize_results,
105 'fwknop-path=s' => \$fwknopCmd,
106 'fwknopd-path=s' => \$fwknopdCmd,
107 'libfko-path=s' => \$libfko_bin,
108 'loopback-intf=s' => \$loopback_intf,
109 'test-include=s' => \$test_include,
110 'include=s' => \$test_include, ### synonym
111 'test-exclude=s' => \$test_exclude,
112 'exclude=s' => \$test_exclude, ### synonym
113 'enable-recompile-check' => \$enable_recompilation_warnings_check,
114 'List-mode' => \$list_mode,
115 'enable-valgrind' => \$use_valgrind,
116 'valgrind-path=s' => \$valgrindCmd,
117 'diff' => \$diff_mode,
123 ### create an anonymized tar file of test suite results that can be
124 ### emailed around to assist in debugging fwknop communications
125 exit &anonymize_results() if $anonymize_results;
127 &identify_loopback_intf();
129 $valgrind_str = "$valgrindCmd --leak-check=full " .
130 "--show-reachable=yes --track-origins=yes" if $use_valgrind;
132 my $intf_str = "-i $loopback_intf --foreground --verbose --verbose";
134 my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
135 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
136 "$local_key_file --verbose --verbose";
138 my $default_client_gpg_args = "$default_client_args " .
139 "--gpg-recipient-key $gpg_server_key " .
140 "--gpg-signer-key $gpg_client_key " .
141 "--gpg-home-dir $gpg_client_home_dir";
143 my $default_server_conf_args = "-c $default_conf -a $default_access_conf " .
144 "-d $default_digest_file -p $default_pid_file";
146 my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " .
147 "$valgrind_str $fwknopdCmd -c $default_conf " .
148 "-a $gpg_access_conf $intf_str " .
149 "-d $default_digest_file -p $default_pid_file";
151 ### point the compiled binaries at the local libary path
152 ### instead of any installed libfko instance
153 $ENV{'LD_LIBRARY_PATH'} = $lib_dir;
155 ### main array that defines the tests we will run
158 'category' => 'recompilation',
159 'detail' => 'recompile and look for compilation warnings',
160 'err_msg' => 'compile warnings exist',
161 'function' => \&compile_warnings,
165 'category' => 'build',
166 'subcategory' => 'client',
167 'detail' => 'binary exists',
168 'err_msg' => 'binary not found',
169 'function' => \&binary_exists,
170 'binary' => $fwknopCmd,
174 'category' => 'build security',
175 'subcategory' => 'client',
176 'detail' => 'Position Independent Executable (PIE)',
177 'err_msg' => 'non PIE binary (fwknop client)',
178 'function' => \&pie_binary,
179 'binary' => $fwknopCmd,
183 'category' => 'build security',
184 'subcategory' => 'client',
185 'detail' => 'stack protected binary',
186 'err_msg' => 'non stack protected binary (fwknop client)',
187 'function' => \&stack_protected_binary,
188 'binary' => $fwknopCmd,
192 'category' => 'build security',
193 'subcategory' => 'client',
194 'detail' => 'fortify source functions',
195 'err_msg' => 'source functions not fortified (fwknop client)',
196 'function' => \&fortify_source_functions,
197 'binary' => $fwknopCmd,
201 'category' => 'build security',
202 'subcategory' => 'client',
203 'detail' => 'read-only relocations',
204 'err_msg' => 'no read-only relocations (fwknop client)',
205 'function' => \&read_only_relocations,
206 'binary' => $fwknopCmd,
210 'category' => 'build security',
211 'subcategory' => 'client',
212 'detail' => 'immediate binding',
213 'err_msg' => 'no immediate binding (fwknop client)',
214 'function' => \&immediate_binding,
215 'binary' => $fwknopCmd,
220 'category' => 'build',
221 'subcategory' => 'server',
222 'detail' => 'binary exists',
223 'err_msg' => 'binary not found',
224 'function' => \&binary_exists,
225 'binary' => $fwknopdCmd,
230 'category' => 'build security',
231 'subcategory' => 'server',
232 'detail' => 'Position Independent Executable (PIE)',
233 'err_msg' => 'non PIE binary (fwknopd server)',
234 'function' => \&pie_binary,
235 'binary' => $fwknopdCmd,
239 'category' => 'build security',
240 'subcategory' => 'server',
241 'detail' => 'stack protected binary',
242 'err_msg' => 'non stack protected binary (fwknopd server)',
243 'function' => \&stack_protected_binary,
244 'binary' => $fwknopdCmd,
248 'category' => 'build security',
249 'subcategory' => 'server',
250 'detail' => 'fortify source functions',
251 'err_msg' => 'source functions not fortified (fwknopd server)',
252 'function' => \&fortify_source_functions,
253 'binary' => $fwknopdCmd,
257 'category' => 'build security',
258 'subcategory' => 'server',
259 'detail' => 'read-only relocations',
260 'err_msg' => 'no read-only relocations (fwknopd server)',
261 'function' => \&read_only_relocations,
262 'binary' => $fwknopdCmd,
266 'category' => 'build security',
267 'subcategory' => 'server',
268 'detail' => 'immediate binding',
269 'err_msg' => 'no immediate binding (fwknopd server)',
270 'function' => \&immediate_binding,
271 'binary' => $fwknopdCmd,
276 'category' => 'build',
277 'subcategory' => 'libfko',
278 'detail' => 'binary exists',
279 'err_msg' => 'binary not found',
280 'function' => \&binary_exists,
281 'binary' => $libfko_bin,
285 'category' => 'build security',
286 'subcategory' => 'libfko',
287 'detail' => 'stack protected binary',
288 'err_msg' => 'non stack protected binary (libfko)',
289 'function' => \&stack_protected_binary,
290 'binary' => $libfko_bin,
294 'category' => 'build security',
295 'subcategory' => 'libfko',
296 'detail' => 'fortify source functions',
297 'err_msg' => 'source functions not fortified (libfko)',
298 'function' => \&fortify_source_functions,
299 'binary' => $libfko_bin,
303 'category' => 'build security',
304 'subcategory' => 'libfko',
305 'detail' => 'read-only relocations',
306 'err_msg' => 'no read-only relocations (libfko)',
307 'function' => \&read_only_relocations,
308 'binary' => $libfko_bin,
312 'category' => 'build security',
313 'subcategory' => 'libfko',
314 'detail' => 'immediate binding',
315 'err_msg' => 'no immediate binding (libfko)',
316 'function' => \&immediate_binding,
317 'binary' => $libfko_bin,
322 'category' => 'preliminaries',
323 'subcategory' => 'client',
324 'detail' => 'usage info',
325 'err_msg' => 'could not get usage info',
326 'function' => \&generic_exec,
327 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd -h",
331 'category' => 'preliminaries',
332 'subcategory' => 'client',
333 'detail' => 'getopt() no such argument',
334 'err_msg' => 'getopt() allowed non-existant argument',
335 'function' => \&generic_exec,
336 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --no-such-arg",
341 'category' => 'preliminaries',
342 'subcategory' => 'client',
343 'detail' => '--test mode, packet not sent',
344 'err_msg' => '--test mode, packet sent?',
345 'function' => \&generic_exec,
346 'positive_output_matches' => [qr/test\smode\senabled/],
347 'cmdline' => "$default_client_args --test",
352 'category' => 'preliminaries',
353 'subcategory' => 'client',
354 'detail' => 'expected code version',
355 'err_msg' => 'code version mis-match',
356 'function' => \&expected_code_version,
357 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopCmd --version",
362 'category' => 'preliminaries',
363 'subcategory' => 'server',
364 'detail' => 'usage info',
365 'err_msg' => 'could not get usage info',
366 'function' => \&generic_exec,
367 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd -h",
371 'category' => 'preliminaries',
372 'subcategory' => 'server',
373 'detail' => 'getopt() no such argument',
374 'err_msg' => 'getopt() allowed non-existant argument',
375 'function' => \&generic_exec,
376 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd --no-such-arg",
382 'category' => 'preliminaries',
383 'subcategory' => 'server',
384 'detail' => 'expected code version',
385 'err_msg' => 'code version mis-match',
386 'function' => \&expected_code_version,
387 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
388 "$fwknopdCmd -c $default_conf -a " .
389 "$default_access_conf --version",
393 'category' => 'preliminaries',
394 'detail' => 'collecting system specifics',
395 'err_msg' => 'could not get complete system specs',
396 'function' => \&specs,
397 'binary' => $fwknopdCmd,
402 'category' => 'basic operations',
403 'detail' => 'dump config',
404 'err_msg' => 'could not dump configuration',
405 'function' => \&generic_exec,
406 'positive_output_matches' => [qr/SYSLOG_IDENTITY/],
408 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
409 "$fwknopdCmd -c $default_conf " .
410 "-a $default_access_conf --dump-config",
414 'category' => 'basic operations',
415 'detail' => 'override config',
416 'err_msg' => 'could not override configuration',
417 'function' => \&generic_exec,
418 'positive_output_matches' => [qr/ENABLE_PCAP_PROMISC.*\'Y\'/],
420 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
421 "$fwknopdCmd $default_server_conf_args " .
422 "-O $conf_dir/override_fwknopd.conf --dump-config",
427 'category' => 'basic operations',
428 'subcategory' => 'client',
429 'detail' => '--get-key path validation',
430 'err_msg' => 'accepted improper --get-key path',
431 'function' => \&generic_exec,
432 'positive_output_matches' => [qr/could\snot\sopen/i],
434 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
435 "$fwknopCmd -A tcp/22 -s $fake_ip " .
436 "-D $loopback_ip --get-key not/there",
440 'category' => 'basic operations',
441 'subcategory' => 'client',
442 'detail' => 'require [-s|-R|-a]',
443 'err_msg' => 'allowed null allow IP',
444 'function' => \&generic_exec,
445 'positive_output_matches' => [qr/must\suse\sone\sof/i],
447 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
448 "$fwknopCmd -D $loopback_ip",
452 'category' => 'basic operations',
453 'subcategory' => 'client',
454 'detail' => '--allow-ip <IP> valid IP',
455 'err_msg' => 'permitted invalid --allow-ip arg',
456 'function' => \&generic_exec,
457 'positive_output_matches' => [qr/Invalid\sallow\sIP\saddress/i],
459 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
460 "$fwknopCmd -A tcp/22 -a invalidIP -D $loopback_ip",
464 'category' => 'basic operations',
465 'subcategory' => 'client',
466 'detail' => '-A <proto>/<port> specification',
467 'err_msg' => 'permitted invalid -A <proto>/<port>',
468 'function' => \&generic_exec,
469 'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
471 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
472 "$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
476 'category' => 'basic operations',
477 'subcategory' => 'client',
478 'detail' => 'generate SPA packet',
479 'err_msg' => 'could not generate SPA packet',
480 'function' => \&client_send_spa_packet,
481 'cmdline' => $default_client_args,
486 'category' => 'basic operations',
487 'subcategory' => 'server',
488 'detail' => 'list current fwknopd fw rules',
489 'err_msg' => 'could not list current fwknopd fw rules',
490 'function' => \&generic_exec,
491 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
492 "$fwknopdCmd $default_server_conf_args --fw-list",
496 'category' => 'basic operations',
497 'subcategory' => 'server',
498 'detail' => 'list all current fw rules',
499 'err_msg' => 'could not list all current fw rules',
500 'function' => \&generic_exec,
501 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
502 "$fwknopdCmd $default_server_conf_args --fw-list-all",
506 'category' => 'basic operations',
507 'subcategory' => 'server',
508 'detail' => 'flush current firewall rules',
509 'err_msg' => 'could not flush current fw rules',
510 'function' => \&generic_exec,
511 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
512 "$fwknopdCmd $default_server_conf_args --fw-flush",
517 'category' => 'basic operations',
518 'subcategory' => 'server',
520 'err_msg' => 'start error',
521 'function' => \&server_start,
522 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
523 "$fwknopdCmd $default_server_conf_args $intf_str",
527 'category' => 'basic operations',
528 'subcategory' => 'server',
530 'err_msg' => 'stop error',
531 'function' => \&server_stop,
532 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
533 "$fwknopdCmd $default_server_conf_args $intf_str",
537 'category' => 'basic operations',
538 'subcategory' => 'server',
539 'detail' => 'write PID',
540 'err_msg' => 'did not write PID',
541 'function' => \&write_pid,
542 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
543 "$fwknopdCmd $default_server_conf_args $intf_str",
548 'category' => 'basic operations',
549 'subcategory' => 'server',
550 'detail' => '--packet-limit 1 exit',
551 'err_msg' => 'did not exit after one packet',
552 'function' => \&server_packet_limit,
553 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
554 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
558 'category' => 'basic operations',
559 'subcategory' => 'server',
560 'detail' => 'ignore packets < min SPA len (140)',
561 'err_msg' => 'did not ignore small packets',
562 'function' => \&server_ignore_small_packets,
563 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
564 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str",
568 'category' => 'basic operations',
569 'subcategory' => 'server',
570 'detail' => '-P bpf filter ignore packet',
571 'err_msg' => 'filter did not ignore packet',
572 'function' => \&server_bpf_ignore_packet,
573 'cmdline' => $default_client_args,
574 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
575 "$fwknopdCmd $default_server_conf_args --packet-limit 1 $intf_str " .
576 qq|-P "udp port $non_std_spa_port"|,
581 'category' => 'Rijndael SPA',
582 'subcategory' => 'client+server',
583 'detail' => 'complete cycle (tcp/22 ssh)',
584 'err_msg' => 'could not complete SPA cycle',
585 'function' => \&spa_cycle,
586 'cmdline' => $default_client_args,
587 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
588 "$fwknopdCmd $default_server_conf_args $intf_str",
589 'fw_rule_created' => $NEW_RULE_REQUIRED,
590 'fw_rule_removed' => $NEW_RULE_REMOVED,
594 'category' => 'Rijndael SPA',
595 'subcategory' => 'client+server',
596 'detail' => 'packet aging (past) (tcp/22 ssh)',
597 'err_msg' => 'old SPA packet accepted',
598 'function' => \&spa_cycle,
599 'cmdline' => "$default_client_args --time-offset-minus 300s",
600 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
601 "$fwknopdCmd $default_server_conf_args $intf_str",
602 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
603 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
607 'category' => 'Rijndael SPA',
608 'subcategory' => 'client+server',
609 'detail' => 'packet aging (future) (tcp/22 ssh)',
610 'err_msg' => 'future SPA packet accepted',
611 'function' => \&spa_cycle,
612 'cmdline' => "$default_client_args --time-offset-plus 300s",
613 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
614 "$fwknopdCmd $default_server_conf_args $intf_str",
615 'server_positive_output_matches' => [qr/SPA\sdata\stime\sdifference/],
616 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
620 'category' => 'Rijndael SPA',
621 'subcategory' => 'client+server',
622 'detail' => 'expired stanza (tcp/22 ssh)',
623 'err_msg' => 'SPA packet accepted',
624 'function' => \&spa_cycle,
625 'cmdline' => $default_client_args,
626 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
627 "$fwknopdCmd -c $default_conf -a $expired_access_conf " .
628 "-d $default_digest_file -p $default_pid_file $intf_str",
629 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
630 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
634 'category' => 'Rijndael SPA',
635 'subcategory' => 'client+server',
636 'detail' => 'invalid expire date (tcp/22 ssh)',
637 'err_msg' => 'SPA packet accepted',
638 'function' => \&spa_cycle,
639 'cmdline' => $default_client_args,
640 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
641 "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
642 "-d $default_digest_file -p $default_pid_file $intf_str",
643 'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
644 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
648 'category' => 'Rijndael SPA',
649 'subcategory' => 'client+server',
650 'detail' => 'expired epoch stanza (tcp/22 ssh)',
651 'err_msg' => 'SPA packet accepted',
652 'function' => \&spa_cycle,
653 'cmdline' => $default_client_args,
654 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
655 "$fwknopdCmd -c $default_conf -a $expired_epoch_access_conf " .
656 "-d $default_digest_file -p $default_pid_file $intf_str",
657 'server_positive_output_matches' => [qr/Access\sstanza\shas\sexpired/],
658 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
662 'category' => 'Rijndael SPA',
663 'subcategory' => 'client+server',
664 'detail' => 'future expired stanza (tcp/22 ssh)',
665 'err_msg' => 'SPA packet not accepted',
666 'function' => \&spa_cycle,
667 'cmdline' => $default_client_args,
668 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
669 "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
670 "-d $default_digest_file -p $default_pid_file $intf_str",
671 'fw_rule_created' => $NEW_RULE_REQUIRED,
672 'fw_rule_removed' => $NEW_RULE_REMOVED,
677 'category' => 'Rijndael SPA',
678 'subcategory' => 'client+server',
679 'detail' => 'OPEN_PORTS (tcp/22 ssh)',
680 'err_msg' => "improper OPEN_PORTS result",
681 'function' => \&spa_cycle,
682 'cmdline' => $default_client_args,
683 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
684 "$fwknopdCmd -c $default_conf -a $open_ports_access_conf " .
685 "-d $default_digest_file -p $default_pid_file $intf_str",
686 'fw_rule_created' => $NEW_RULE_REQUIRED,
687 'fw_rule_removed' => $NEW_RULE_REMOVED,
691 'category' => 'Rijndael SPA',
692 'subcategory' => 'client+server',
693 'detail' => 'OPEN_PORTS mismatch',
694 'err_msg' => "SPA packet accepted",
695 'function' => \&spa_cycle,
696 'cmdline' => $default_client_args,
697 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
698 "$fwknopdCmd -c $default_conf -a $mismatch_open_ports_access_conf " .
699 "-d $default_digest_file -p $default_pid_file $intf_str",
700 'server_positive_output_matches' => [qr/One\s+or\s+more\s+requested/],
701 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
705 'category' => 'Rijndael SPA',
706 'subcategory' => 'client+server',
707 'detail' => 'require user (tcp/22 ssh)',
708 'err_msg' => "missed require user criteria",
709 'function' => \&spa_cycle,
710 'cmdline' => "SPOOF_USER=$spoof_user $default_client_args",
711 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
712 "$fwknopdCmd -c $default_conf -a $require_user_access_conf " .
713 "-d $default_digest_file -p $default_pid_file $intf_str",
714 'fw_rule_created' => $NEW_RULE_REQUIRED,
715 'fw_rule_removed' => $NEW_RULE_REMOVED,
719 'category' => 'Rijndael SPA',
720 'subcategory' => 'client+server',
721 'detail' => 'user mismatch (tcp/22 ssh)',
722 'err_msg' => "improper user accepted for access",
723 'function' => \&user_mismatch,
724 'function' => \&spa_cycle,
725 'cmdline' => $default_client_args,
726 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
727 "$fwknopdCmd -c $default_conf -a $mismatch_user_access_conf " .
728 "-d $default_digest_file -p $default_pid_file $intf_str",
729 'server_positive_output_matches' => [qr/Username\s+in\s+SPA\s+data/],
730 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
734 'category' => 'Rijndael SPA',
735 'subcategory' => 'client+server',
736 'detail' => 'require src (tcp/22 ssh)',
737 'err_msg' => "fw rule not created",
738 'function' => \&spa_cycle,
739 'cmdline' => $default_client_args,
740 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
741 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
742 "-d $default_digest_file -p $default_pid_file $intf_str",
743 'fw_rule_created' => $NEW_RULE_REQUIRED,
744 'fw_rule_removed' => $NEW_RULE_REMOVED,
748 'category' => 'Rijndael SPA',
749 'subcategory' => 'client+server',
750 'detail' => 'mismatch require src (tcp/22 ssh)',
751 'err_msg' => "fw rule created",
752 'function' => \&spa_cycle,
753 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
754 "$fwknopCmd -A tcp/22 -s -D $loopback_ip --get-key " .
755 "$local_key_file --verbose --verbose",
756 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
757 "$fwknopdCmd -c $default_conf -a $require_src_access_conf " .
758 "-d $default_digest_file -p $default_pid_file $intf_str",
759 'server_positive_output_matches' => [qr/Got\s0.0.0.0\swhen\svalid\ssource\sIP/],
760 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
765 'category' => 'Rijndael SPA',
766 'subcategory' => 'client+server',
767 'detail' => 'IP filtering (tcp/22 ssh)',
768 'err_msg' => "did not filter $loopback_ip",
769 'function' => \&spa_cycle,
770 'cmdline' => $default_client_args,
771 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
772 "$fwknopdCmd -c $default_conf -a $no_source_match_access_conf " .
773 "-d $default_digest_file -p $default_pid_file $intf_str",
774 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
775 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
779 'category' => 'Rijndael SPA',
780 'subcategory' => 'client+server',
781 'detail' => 'subnet filtering (tcp/22 ssh)',
782 'err_msg' => "did not filter $loopback_ip",
783 'function' => \&spa_cycle,
784 'cmdline' => $default_client_args,
785 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
786 "$fwknopdCmd -c $default_conf -a $no_subnet_source_match_access_conf " .
787 "-d $default_digest_file -p $default_pid_file $intf_str",
788 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
789 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
793 'category' => 'Rijndael SPA',
794 'subcategory' => 'client+server',
795 'detail' => 'IP+subnet filtering (tcp/22 ssh)',
796 'err_msg' => "did not filter $loopback_ip",
797 'function' => \&spa_cycle,
798 'cmdline' => $default_client_args,
799 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
800 "$fwknopdCmd -c $default_conf -a $no_multi_source_match_access_conf " .
801 "-d $default_digest_file -p $default_pid_file $intf_str",
802 'server_positive_output_matches' => [qr/No\saccess\sdata\sfound/],
803 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
807 'category' => 'Rijndael SPA',
808 'subcategory' => 'client+server',
809 'detail' => 'IP match (tcp/22 ssh)',
810 'err_msg' => "did not filter $loopback_ip",
811 'function' => \&spa_cycle,
812 'cmdline' => $default_client_args,
813 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
814 "$fwknopdCmd -c $default_conf -a $ip_source_match_access_conf " .
815 "-d $default_digest_file -p $default_pid_file $intf_str",
816 'fw_rule_created' => $NEW_RULE_REQUIRED,
817 'fw_rule_removed' => $NEW_RULE_REMOVED,
821 'category' => 'Rijndael SPA',
822 'subcategory' => 'client+server',
823 'detail' => 'subnet match (tcp/22 ssh)',
824 'err_msg' => "did not filter $loopback_ip",
825 'function' => \&spa_cycle,
826 'cmdline' => $default_client_args,
827 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
828 "$fwknopdCmd -c $default_conf -a $subnet_source_match_access_conf " .
829 "-d $default_digest_file -p $default_pid_file $intf_str",
830 'fw_rule_created' => $NEW_RULE_REQUIRED,
831 'fw_rule_removed' => $NEW_RULE_REMOVED,
835 'category' => 'Rijndael SPA',
836 'subcategory' => 'client+server',
837 'detail' => 'multi IP/net match (tcp/22 ssh)',
838 'err_msg' => "did not filter $loopback_ip",
839 'function' => \&spa_cycle,
840 'cmdline' => $default_client_args,
841 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
842 "$fwknopdCmd -c $default_conf -a $multi_source_match_access_conf " .
843 "-d $default_digest_file -p $default_pid_file $intf_str",
844 'fw_rule_created' => $NEW_RULE_REQUIRED,
845 'fw_rule_removed' => $NEW_RULE_REMOVED,
849 'category' => 'Rijndael SPA',
850 'subcategory' => 'client+server',
851 'detail' => 'multi access stanzas (tcp/22 ssh)',
852 'err_msg' => "could not complete SPA cycle",
853 'function' => \&spa_cycle,
854 'cmdline' => $default_client_args,
855 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
856 "$fwknopdCmd -c $default_conf -a $multi_stanzas_access_conf " .
857 "-d $default_digest_file -p $default_pid_file $intf_str",
858 'fw_rule_created' => $NEW_RULE_REQUIRED,
859 'fw_rule_removed' => $NEW_RULE_REMOVED,
863 'category' => 'Rijndael SPA',
864 'subcategory' => 'client+server',
865 'detail' => 'bad/good key stanzas (tcp/22 ssh)',
866 'err_msg' => "could not complete SPA cycle",
867 'function' => \&spa_cycle,
868 'cmdline' => $default_client_args,
869 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
870 "$fwknopdCmd -c $default_conf -a $multi_stanzas_with_broken_keys_conf " .
871 "-d $default_digest_file -p $default_pid_file $intf_str",
872 'fw_rule_created' => $NEW_RULE_REQUIRED,
873 'fw_rule_removed' => $NEW_RULE_REMOVED,
878 'category' => 'Rijndael SPA',
879 'subcategory' => 'client+server',
880 'detail' => "non-enabled NAT (tcp/22 ssh)",
881 'err_msg' => "SPA packet not filtered",
882 'function' => \&spa_cycle,
883 'cmdline' => "$default_client_args -N $internal_nat_host:22",
884 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
885 "$fwknopdCmd $default_server_conf_args $intf_str",
886 'server_positive_output_matches' => [qr/requested\sNAT\saccess.*not\senabled/i],
887 'server_conf' => $nat_conf,
888 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
892 'category' => 'Rijndael SPA',
893 'subcategory' => 'client+server',
894 'detail' => "NAT to $internal_nat_host (tcp/22 ssh)",
895 'err_msg' => "could not complete NAT SPA cycle",
896 'function' => \&spa_cycle,
897 'cmdline' => "$default_client_args -N $internal_nat_host:22",
898 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
899 "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
900 "-d $default_digest_file -p $default_pid_file $intf_str",
901 'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
902 'fw_rule_created' => $NEW_RULE_REQUIRED,
903 'fw_rule_removed' => $NEW_RULE_REMOVED,
904 'server_conf' => $nat_conf,
909 'category' => 'Rijndael SPA',
910 'subcategory' => 'client+server',
911 'detail' => "force NAT $force_nat_host (tcp/22 ssh)",
912 'err_msg' => "could not complete NAT SPA cycle",
913 'function' => \&spa_cycle,
914 'cmdline' => $default_client_args,
915 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
916 "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
917 "-d $default_digest_file -p $default_pid_file $intf_str",
918 'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
919 'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
920 'fw_rule_created' => $NEW_RULE_REQUIRED,
921 'fw_rule_removed' => $NEW_RULE_REMOVED,
922 'server_conf' => $nat_conf,
927 'category' => 'Rijndael SPA',
928 'subcategory' => 'client+server',
929 'detail' => 'complete cycle (tcp/23 telnet)',
930 'err_msg' => 'could not complete SPA cycle',
931 'function' => \&spa_cycle,
932 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
933 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
934 "$local_key_file --verbose --verbose",
935 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
936 "$fwknopdCmd $default_server_conf_args $intf_str",
937 'fw_rule_created' => $NEW_RULE_REQUIRED,
938 'fw_rule_removed' => $NEW_RULE_REMOVED,
942 'category' => 'Rijndael SPA',
943 'subcategory' => 'client+server',
944 'detail' => 'complete cycle (tcp/9418 git)',
945 'err_msg' => 'could not complete SPA cycle',
946 'function' => \&spa_cycle,
947 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
948 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
949 "$local_key_file --verbose --verbose",
950 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
951 "$fwknopdCmd $default_server_conf_args $intf_str",
952 'fw_rule_created' => $NEW_RULE_REQUIRED,
953 'fw_rule_removed' => $NEW_RULE_REMOVED,
957 'category' => 'Rijndael SPA',
958 'subcategory' => 'client+server',
959 'detail' => 'complete cycle (udp/53 dns)',
960 'err_msg' => 'could not complete SPA cycle',
961 'function' => \&spa_cycle,
962 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
963 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
964 "$local_key_file --verbose --verbose",
965 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
966 "$fwknopdCmd $default_server_conf_args $intf_str",
967 'fw_rule_created' => $NEW_RULE_REQUIRED,
968 'fw_rule_removed' => $NEW_RULE_REMOVED,
972 'category' => 'Rijndael SPA',
973 'subcategory' => 'client+server',
974 'detail' => "-P bpf SPA over port $non_std_spa_port",
975 'err_msg' => 'could not complete SPA cycle',
976 'function' => \&spa_cycle,
977 'cmdline' => "$default_client_args --server-port $non_std_spa_port",
978 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
979 "$fwknopdCmd $default_server_conf_args $intf_str " .
980 qq|-P "udp port $non_std_spa_port"|,
981 'server_positive_output_matches' => [qr/PCAP\sfilter.*\s$non_std_spa_port/],
982 'fw_rule_created' => $NEW_RULE_REQUIRED,
983 'fw_rule_removed' => $NEW_RULE_REMOVED,
988 'category' => 'Rijndael SPA',
989 'subcategory' => 'client+server',
990 'detail' => 'random SPA port (tcp/22 ssh)',
991 'err_msg' => 'could not complete SPA cycle',
992 'function' => \&spa_cycle,
993 'cmdline' => "$default_client_args -r",
994 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
995 "$fwknopdCmd $default_server_conf_args $intf_str " .
997 'fw_rule_created' => $NEW_RULE_REQUIRED,
998 'fw_rule_removed' => $NEW_RULE_REMOVED,
1003 'category' => 'Rijndael SPA',
1004 'subcategory' => 'client+server',
1005 'detail' => 'spoof username (tcp/22)',
1006 'err_msg' => 'could not spoof username',
1007 'function' => \&spoof_username,
1008 'cmdline' => "SPOOF_USER=$spoof_user LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1009 "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " .
1010 "$local_key_file --verbose --verbose",
1011 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1012 "$fwknopdCmd $default_server_conf_args $intf_str",
1017 'category' => 'Rijndael SPA',
1018 'subcategory' => 'client+server',
1019 'detail' => 'replay attack detection',
1020 'err_msg' => 'could not detect replay attack',
1021 'function' => \&replay_detection,
1022 'cmdline' => $default_client_args,
1023 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1024 "$fwknopdCmd $default_server_conf_args $intf_str",
1028 'category' => 'Rijndael SPA',
1029 'subcategory' => 'server',
1030 'detail' => 'digest cache structure',
1031 'err_msg' => 'improper digest cache structure',
1032 'function' => \&digest_cache_structure,
1037 'category' => 'Rijndael SPA',
1038 'subcategory' => 'client+server',
1039 'detail' => 'non-base64 altered SPA data',
1040 'err_msg' => 'allowed improper SPA data',
1041 'function' => \&altered_non_base64_spa_data,
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' => 'client+server',
1050 'detail' => 'base64 altered SPA data',
1051 'err_msg' => 'allowed improper SPA data',
1052 'function' => \&altered_base64_spa_data,
1053 'cmdline' => $default_client_args,
1054 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1055 "$fwknopdCmd $default_server_conf_args $intf_str",
1059 'category' => 'Rijndael SPA',
1060 'subcategory' => 'client+server',
1061 'detail' => 'appended data to SPA pkt',
1062 'err_msg' => 'allowed improper SPA data',
1063 'function' => \&appended_spa_data,
1064 'cmdline' => $default_client_args,
1065 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1066 "$fwknopdCmd $default_server_conf_args $intf_str",
1070 'category' => 'Rijndael SPA',
1071 'subcategory' => 'client+server',
1072 'detail' => 'prepended data to SPA pkt',
1073 'err_msg' => 'allowed improper SPA data',
1074 'function' => \&prepended_spa_data,
1075 'cmdline' => $default_client_args,
1076 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1077 "$fwknopdCmd $default_server_conf_args $intf_str",
1082 'category' => 'GnuPG (GPG) SPA',
1083 'subcategory' => 'client+server',
1084 'detail' => 'complete cycle (tcp/22 ssh)',
1085 'err_msg' => 'could not complete SPA cycle',
1086 'function' => \&spa_cycle,
1087 'cmdline' => $default_client_gpg_args,
1088 'fwknopd_cmdline' => $default_server_gpg_args,
1089 'fw_rule_created' => $NEW_RULE_REQUIRED,
1090 'fw_rule_removed' => $NEW_RULE_REMOVED,
1094 'category' => 'GnuPG (GPG) SPA',
1095 'subcategory' => 'client+server',
1096 'detail' => 'multi gpg-IDs (tcp/22 ssh)',
1097 'err_msg' => 'could not complete SPA cycle',
1098 'function' => \&spa_cycle,
1099 'cmdline' => $default_client_gpg_args,
1100 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " .
1101 "$valgrind_str $fwknopdCmd -c $default_conf " .
1102 "-a $multi_gpg_access_conf $intf_str " .
1103 "-d $default_digest_file -p $default_pid_file",
1104 'fw_rule_created' => $NEW_RULE_REQUIRED,
1105 'fw_rule_removed' => $NEW_RULE_REMOVED,
1110 'category' => 'GnuPG (GPG) SPA',
1111 'subcategory' => 'client+server',
1112 'detail' => 'complete cycle (tcp/23 telnet)',
1113 'err_msg' => 'could not complete SPA cycle',
1114 'function' => \&spa_cycle,
1115 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1116 "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " .
1117 "$local_key_file --verbose --verbose " .
1118 "--gpg-recipient-key $gpg_server_key " .
1119 "--gpg-signer-key $gpg_client_key " .
1120 "--gpg-home-dir $gpg_client_home_dir",
1121 'fwknopd_cmdline' => $default_server_gpg_args,
1122 'fw_rule_created' => $NEW_RULE_REQUIRED,
1123 'fw_rule_removed' => $NEW_RULE_REMOVED,
1127 'category' => 'GnuPG (GPG) SPA',
1128 'subcategory' => 'client+server',
1129 'detail' => 'complete cycle (tcp/9418 git)',
1130 'err_msg' => 'could not complete SPA cycle',
1131 'function' => \&spa_cycle,
1132 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1133 "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " .
1134 "$local_key_file --verbose --verbose " .
1135 "--gpg-recipient-key $gpg_server_key " .
1136 "--gpg-signer-key $gpg_client_key " .
1137 "--gpg-home-dir $gpg_client_home_dir",
1138 'fwknopd_cmdline' => $default_server_gpg_args,
1139 'fw_rule_created' => $NEW_RULE_REQUIRED,
1140 'fw_rule_removed' => $NEW_RULE_REMOVED,
1144 'category' => 'GnuPG (GPG) SPA',
1145 'subcategory' => 'client+server',
1146 'detail' => 'complete cycle (udp/53 dns)',
1147 'err_msg' => 'could not complete SPA cycle',
1148 'function' => \&spa_cycle,
1149 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
1150 "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " .
1151 "$local_key_file --verbose --verbose " .
1152 "--gpg-recipient-key $gpg_server_key " .
1153 "--gpg-signer-key $gpg_client_key " .
1154 "--gpg-home-dir $gpg_client_home_dir",
1155 'fwknopd_cmdline' => $default_server_gpg_args,
1156 'fw_rule_created' => $NEW_RULE_REQUIRED,
1157 'fw_rule_removed' => $NEW_RULE_REMOVED,
1162 'category' => 'GnuPG (GPG) SPA',
1163 'subcategory' => 'client+server',
1164 'detail' => 'replay attack detection',
1165 'err_msg' => 'could not detect replay attack',
1166 'function' => \&replay_detection,
1167 'cmdline' => $default_client_gpg_args,
1168 'fwknopd_cmdline' => $default_server_gpg_args,
1173 'category' => 'GnuPG (GPG) SPA',
1174 'subcategory' => 'client+server',
1175 'detail' => 'non-base64 altered SPA data',
1176 'err_msg' => 'allowed improper SPA data',
1177 'function' => \&altered_non_base64_spa_data,
1178 'cmdline' => $default_client_gpg_args,
1179 'fwknopd_cmdline' => $default_server_gpg_args,
1183 'category' => 'GnuPG (GPG) SPA',
1184 'subcategory' => 'client+server',
1185 'detail' => 'base64 altered SPA data',
1186 'err_msg' => 'allowed improper SPA data',
1187 'function' => \&altered_base64_spa_data,
1188 'cmdline' => $default_client_gpg_args,
1189 'fwknopd_cmdline' => $default_server_gpg_args,
1193 'category' => 'GnuPG (GPG) SPA',
1194 'subcategory' => 'client+server',
1195 'detail' => 'appended data to SPA pkt',
1196 'err_msg' => 'allowed improper SPA data',
1197 'function' => \&appended_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' => 'prepended data to SPA pkt',
1206 'err_msg' => 'allowed improper SPA data',
1207 'function' => \&prepended_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' => 'spoof username (tcp/22 ssh)',
1216 'err_msg' => 'could not spoof username',
1217 'function' => \&spoof_username,
1218 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args",
1219 'fwknopd_cmdline' => $default_server_gpg_args,
1224 'category' => 'GnuPG (GPG) SPA',
1225 'subcategory' => 'server',
1226 'detail' => 'digest cache structure',
1227 'err_msg' => 'improper digest cache structure',
1228 'function' => \&digest_cache_structure,
1234 'category' => $REQUIRED,
1235 'subcategory' => $OPTIONAL,
1236 'detail' => $REQUIRED,
1237 'function' => $REQUIRED,
1238 'binary' => $OPTIONAL,
1239 'cmdline' => $OPTIONAL,
1240 'fwknopd_cmdline' => $OPTIONAL,
1241 'fatal' => $OPTIONAL,
1242 'exec_err' => $OPTIONAL,
1243 'fw_rule_created' => $OPTIONAL,
1244 'fw_rule_removed' => $OPTIONAL,
1245 'server_conf' => $OPTIONAL,
1246 'positive_output_matches' => $OPTIONAL,
1247 'negative_output_matches' => $OPTIONAL,
1248 'server_positive_output_matches' => $OPTIONAL,
1249 'server_negative_output_matches' => $OPTIONAL,
1253 &diff_test_results();
1257 ### make sure everything looks as expected before continuing
1260 &logr("\n[+] Starting the fwknop test suite...\n\n" .
1261 " args: @args_cp\n\n"
1264 ### save the results from any previous test suite run
1265 ### so that we can potentially compare them with --diff
1266 if ($saved_last_results) {
1267 &logr(" Saved results from previous run " .
1268 "to: ${output_dir}.last/\n\n");
1271 ### main loop through all of the tests
1272 for my $test_hr (@tests) {
1273 &run_test($test_hr);
1276 &logr("\n[+] passed/failed/executed: $passed/$failed/$executed tests\n\n");
1278 copy $logfile, "$output_dir/$logfile" or die $!;
1282 #===================== end main =======================
1285 my $test_hr = shift;
1287 my $msg = "[$test_hr->{'category'}]";
1288 $msg .= " [$test_hr->{'subcategory'}]" if $test_hr->{'subcategory'};
1289 $msg .= " $test_hr->{'detail'}";
1291 return unless &process_include_exclude($msg);
1301 $current_test_file = "$output_dir/$executed.test";
1302 $server_test_file = "$output_dir/${executed}_fwknopd.test";
1304 &write_test_file("[+] TEST: $msg\n", $current_test_file);
1305 $test_hr->{'msg'} = $msg;
1306 if (&{$test_hr->{'function'}}($test_hr)) {
1307 &logr("pass ($executed)\n");
1310 &logr("fail ($executed)\n");
1313 if ($test_hr->{'fatal'} eq $YES) {
1314 die "[*] required test failed, exiting.";
1321 sub process_include_exclude() {
1324 ### inclusions/exclusions
1325 if (@tests_to_include) {
1327 for my $test (@tests_to_include) {
1328 if ($msg =~ /$test/) {
1333 return 0 unless $found;
1335 if (@tests_to_exclude) {
1337 for my $test (@tests_to_exclude) {
1338 if ($msg =~ /$test/) {
1348 sub diff_test_results() {
1349 die "[*] Need results from a previous run before running --diff"
1350 unless -d "${output_dir}.last";
1351 die "[*] Current results set does not exist." unless -d $output_dir;
1353 my %current_tests = ();
1354 my %previous_tests = ();
1356 ### Only diff results for matching tests (parse the logfile to see which
1357 ### test numbers match across the two test cycles).
1358 &build_results_hash(\%current_tests, $output_dir);
1359 &build_results_hash(\%previous_tests, "${output_dir}.last");
1361 for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
1362 keys %current_tests) {
1363 my $current_result = $current_tests{$test_msg}{'pass_fail'};
1364 my $current_num = $current_tests{$test_msg}{'num'};
1365 if (defined $previous_tests{$test_msg}) {
1366 print "[+] Checking: $test_msg\n";
1367 my $previous_result = $previous_tests{$test_msg}{'pass_fail'};
1368 my $previous_num = $previous_tests{$test_msg}{'num'};
1369 if ($current_result ne $previous_result) {
1370 print " DIFF: **$current_result** $test_msg\n";
1373 &diff_results($previous_num, $current_num);
1381 sub diff_results() {
1382 my ($previous_num, $current_num) = @_;
1384 ### edit out any valgrind "==354==" prefixes
1385 my $valgrind_search_re = qr/^==\d+==\s/;
1387 ### remove CMD timestamps
1388 my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
1390 for my $file ("${output_dir}.last/${previous_num}.test",
1391 "${output_dir}.last/${previous_num}_fwknopd.test",
1392 "${output_dir}/${current_num}.test",
1393 "${output_dir}/${current_num}_fwknopd.test",
1395 system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
1396 system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
1399 if (-e "${output_dir}.last/${previous_num}.test"
1400 and -e "${output_dir}/${current_num}.test") {
1401 system "diff -u ${output_dir}.last/${previous_num}.test " .
1402 "${output_dir}/${current_num}.test";
1405 if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
1406 and -e "${output_dir}/${current_num}_fwknopd.test") {
1407 system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
1408 "${output_dir}/${current_num}_fwknopd.test";
1414 sub build_results_hash() {
1415 my ($hr, $dir) = @_;
1417 open F, "< $dir/$logfile" or die $!;
1419 if (/^(.*?)\.\.\..*(pass|fail)\s\((\d+)\)/) {
1420 $hr->{$1}{'pass_fail'} = $2;
1421 $hr->{$1}{'num'} = $3;
1427 sub compile_warnings() {
1429 ### 'make clean' as root
1430 return 0 unless &run_cmd('make -C .. clean',
1431 $cmd_out_tmp, $current_test_file);
1434 my $username = getpwuid((stat($configure_path))[4]);
1435 die "[*] Could not determine $configure_path owner"
1438 return 0 unless &run_cmd("$sudo_path -u $username make -C ..",
1439 $cmd_out_tmp, $current_test_file);
1443 return 0 unless &run_cmd('make -C ..',
1444 $cmd_out_tmp, $current_test_file);
1448 ### look for compilation warnings - something like:
1449 ### warning: ‘test’ is used uninitialized in this function
1450 return 0 if &file_find_regex([qr/\swarning:\s/, qr/gcc\:.*\sunused/], $current_test_file);
1452 ### the new binaries should exist
1453 unless (-e $fwknopCmd and -x $fwknopCmd) {
1454 &write_test_file("[-] $fwknopCmd does not exist or not executable.\n",
1455 $current_test_file);
1457 unless (-e $fwknopdCmd and -x $fwknopdCmd) {
1458 &write_test_file("[-] $fwknopdCmd does not exist or not executable.\n",
1459 $current_test_file);
1465 sub binary_exists() {
1466 my $test_hr = shift;
1467 return 0 unless $test_hr->{'binary'};
1469 ### account for different libfko.so paths (e.g. libfko.so.0.3 with no
1470 ### libfko.so link on OpenBSD)
1472 if ($test_hr->{'binary'} =~ /libfko/) {
1473 unless (-e $test_hr->{'binary'}) {
1474 for my $file (glob("$lib_dir/libfko.so*")) {
1475 if (-e $file and -x $file) {
1476 $test_hr->{'binary'} = $file;
1477 $libfko_bin = $file;
1484 return 0 unless -e $test_hr->{'binary'} and -x $test_hr->{'binary'};
1488 sub expected_code_version() {
1489 my $test_hr = shift;
1491 unless (-e '../VERSION') {
1492 &write_test_file("[-] ../VERSION file does not exist.\n",
1493 $current_test_file);
1497 open F, '< ../VERSION' or die $!;
1500 if ($line =~ /(\d.*\d)/) {
1502 return 0 unless &run_cmd($test_hr->{'cmdline'},
1503 $cmd_out_tmp, $current_test_file);
1504 return 1 if &file_find_regex([qr/$version/], $current_test_file);
1509 sub client_send_spa_packet() {
1510 my $test_hr = shift;
1512 &write_key('fwknoptest', $local_key_file);
1514 return 0 unless &run_cmd($test_hr->{'cmdline'},
1515 $cmd_out_tmp, $current_test_file);
1516 return 0 unless &file_find_regex([qr/final\spacked/i],
1517 $current_test_file);
1523 my $test_hr = shift;
1525 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1526 = &client_server_interaction($test_hr, [], $USE_CLIENT);
1528 if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) {
1529 $rv = 0 unless $fw_rule_created;
1530 } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) {
1531 $rv = 0 if $fw_rule_created;
1534 if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) {
1535 $rv = 0 unless $fw_rule_removed;
1536 } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) {
1537 $rv = 0 if $fw_rule_removed;
1540 if ($test_hr->{'server_positive_output_matches'}) {
1541 $rv = 0 unless &file_find_regex(
1542 $test_hr->{'server_positive_output_matches'},
1546 if ($test_hr->{'server_negative_output_matches'}) {
1547 $rv = 0 if &file_find_regex(
1548 $test_hr->{'server_negative_output_matches'},
1555 sub spoof_username() {
1556 my $test_hr = shift;
1558 my $rv = &spa_cycle($test_hr);
1560 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1561 $current_test_file)) {
1565 unless (&file_find_regex([qr/Username:\s*$spoof_user/],
1566 $server_test_file)) {
1573 sub replay_detection() {
1574 my $test_hr = shift;
1576 ### do a complete SPA cycle and then parse the SPA packet out of the
1577 ### current test file and re-send
1579 return 0 unless &spa_cycle($test_hr);
1581 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1584 &write_test_file("[-] could not get SPA packet " .
1585 "from file: $current_test_file\n",
1586 $current_test_file);
1593 'port' => $default_spa_port,
1594 'dst_ip' => $loopback_ip,
1599 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1600 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1602 $rv = 0 unless $server_was_stopped;
1604 unless (&file_find_regex([qr/Replay\sdetected\sfrom\ssource\sIP/i],
1605 $server_test_file)) {
1612 sub digest_cache_structure() {
1613 my $test_hr = shift;
1616 &run_cmd("file $default_digest_file", $cmd_out_tmp, $current_test_file);
1618 if (&file_find_regex([qr/ASCII/i], $cmd_out_tmp)) {
1620 ### the format should be:
1621 ### <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
1622 open F, "< $default_digest_file" or
1623 die "[*] could not open $default_digest_file: $!";
1627 unless (m|^\S+\s+\d+\s+$ip_re\s+\d+\s+$ip_re\s+\d+\s+\d+|) {
1628 &write_test_file("[-] invalid digest.cache line: $_",
1629 $current_test_file);
1635 } elsif (&file_find_regex([qr/dbm/i], $cmd_out_tmp)) {
1636 &write_test_file("[+] DBM digest file format, " .
1637 "assuming this is valid.\n", $current_test_file);
1639 ### don't know what kind of file the digest.cache is
1640 &write_test_file("[-] unrecognized file type for " .
1641 "$default_digest_file.\n", $current_test_file);
1646 &write_test_file("[+] valid digest.cache structure.\n",
1647 $current_test_file);
1653 sub server_bpf_ignore_packet() {
1654 my $test_hr = shift;
1657 my $server_was_stopped = 0;
1658 my $fw_rule_created = 0;
1659 my $fw_rule_removed = 0;
1661 unless (&client_send_spa_packet($test_hr)) {
1662 &write_test_file("[-] fwknop client execution error.\n",
1663 $current_test_file);
1667 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1670 &write_test_file("[-] could not get SPA packet " .
1671 "from file: $current_test_file\n", $current_test_file);
1678 'port' => $default_spa_port,
1679 'dst_ip' => $loopback_ip,
1684 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1685 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1687 unless (&file_find_regex([qr/PCAP\sfilter.*\s$non_std_spa_port/],
1688 $server_test_file)) {
1695 sub altered_non_base64_spa_data() {
1696 my $test_hr = shift;
1699 my $server_was_stopped = 0;
1700 my $fw_rule_created = 0;
1701 my $fw_rule_removed = 0;
1703 unless (&client_send_spa_packet($test_hr)) {
1704 &write_test_file("[-] fwknop client execution error.\n",
1705 $current_test_file);
1709 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1712 &write_test_file("[-] could not get SPA packet " .
1713 "from file: $current_test_file\n", $current_test_file);
1717 ### alter one byte (change to a ":")
1718 $spa_pkt =~ s|^(.{3}).|$1:|;
1723 'port' => $default_spa_port,
1724 'dst_ip' => $loopback_ip,
1729 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1730 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1732 $rv = 0 unless $server_was_stopped;
1737 sub altered_base64_spa_data() {
1738 my $test_hr = shift;
1741 my $server_was_stopped = 0;
1742 my $fw_rule_created = 0;
1743 my $fw_rule_removed = 0;
1745 unless (&client_send_spa_packet($test_hr)) {
1746 &write_test_file("[-] fwknop client execution error.\n",
1747 $current_test_file);
1751 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1754 &write_test_file("[-] could not get SPA packet " .
1755 "from file: $current_test_file\n", $current_test_file);
1759 $spa_pkt =~ s|^(.{3}).|AAAA|;
1764 'port' => $default_spa_port,
1765 'dst_ip' => $loopback_ip,
1770 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1771 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1773 $rv = 0 unless $server_was_stopped;
1775 if ($fw_rule_created) {
1776 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1779 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1782 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1783 $server_test_file)) {
1790 sub appended_spa_data() {
1791 my $test_hr = shift;
1794 my $server_was_stopped = 0;
1795 my $fw_rule_created = 0;
1796 my $fw_rule_removed = 0;
1798 unless (&client_send_spa_packet($test_hr)) {
1799 &write_test_file("[-] fwknop client execution error.\n",
1800 $current_test_file);
1804 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1807 &write_test_file("[-] could not get SPA packet " .
1808 "from file: $current_test_file\n", $current_test_file);
1817 'port' => $default_spa_port,
1818 'dst_ip' => $loopback_ip,
1823 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1824 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1826 $rv = 0 unless $server_was_stopped;
1828 if ($fw_rule_created) {
1829 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1832 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1835 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1836 $server_test_file)) {
1843 sub prepended_spa_data() {
1844 my $test_hr = shift;
1847 my $server_was_stopped = 0;
1848 my $fw_rule_created = 0;
1849 my $fw_rule_removed = 0;
1851 unless (&client_send_spa_packet($test_hr)) {
1852 &write_test_file("[-] fwknop client execution error.\n",
1853 $current_test_file);
1857 my $spa_pkt = &get_spa_packet_from_file($current_test_file);
1860 &write_test_file("[-] could not get SPA packet " .
1861 "from file: $current_test_file\n", $current_test_file);
1865 $spa_pkt = 'AAAA' . $spa_pkt;
1870 'port' => $default_spa_port,
1871 'dst_ip' => $loopback_ip,
1876 ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1877 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1879 $rv = 0 unless $server_was_stopped;
1881 if ($fw_rule_created) {
1882 &write_test_file("[-] new fw rule created.\n", $current_test_file);
1885 &write_test_file("[+] new fw rule not created.\n", $current_test_file);
1888 unless (&file_find_regex([qr/Error\screating\sfko\scontext/],
1889 $server_test_file)) {
1896 sub server_start() {
1897 my $test_hr = shift;
1899 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1900 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1902 unless (&file_find_regex([qr/Starting\sfwknopd\smain\sevent\sloop/],
1903 $server_test_file)) {
1907 $rv = 0 unless $server_was_stopped;
1913 my $test_hr = shift;
1915 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1916 = &client_server_interaction($test_hr, [], $USE_PREDEF_PKTS);
1918 $rv = 0 unless $server_was_stopped;
1923 sub server_packet_limit() {
1924 my $test_hr = shift;
1929 'port' => $default_spa_port,
1930 'dst_ip' => $loopback_ip,
1935 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1936 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1938 if (&is_fwknopd_running()) {
1943 unless (&file_find_regex([qr/count\slimit\sof\s1\sreached/],
1944 $server_test_file)) {
1948 unless (&file_find_regex([qr/Shutting\sDown\sfwknopd/i],
1949 $server_test_file)) {
1956 sub server_ignore_small_packets() {
1957 my $test_hr = shift;
1962 'port' => $default_spa_port,
1963 'dst_ip' => $loopback_ip,
1964 'data' => 'A'x120, ### < MIN_SPA_DATA_SIZE
1968 my ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
1969 = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
1973 if (&is_fwknopd_running()) {
1981 sub client_server_interaction() {
1982 my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
1985 my $server_was_stopped = 1;
1986 my $fw_rule_created = 1;
1987 my $fw_rule_removed = 0;
1989 ### start fwknopd to monitor for the SPA packet over the loopback interface
1990 my $fwknopd_parent_pid = &start_fwknopd($test_hr);
1992 ### give fwknopd a chance to parse its config and start sniffing
1993 ### on the loopback interface
1994 if ($use_valgrind) {
2000 ### send the SPA packet(s) to the server either manually using IO::Socket or
2001 ### with the fwknopd client
2002 if ($spa_client_flag == $USE_CLIENT) {
2003 unless (&client_send_spa_packet($test_hr)) {
2004 &write_test_file("[-] fwknop client execution error.\n",
2005 $current_test_file);
2009 &send_packets($pkts_hr);
2012 ### check to see if the SPA packet resulted in a new fw access rule
2014 while (not &is_fw_rule_active($test_hr)) {
2015 &write_test_file("[-] new fw rule does not exist.\n",
2016 $current_test_file);
2022 $fw_rule_created = 0;
2023 $fw_rule_removed = 0;
2026 &time_for_valgrind() if $use_valgrind;
2028 if ($fw_rule_created) {
2029 sleep 3; ### allow time for rule time out.
2030 if (&is_fw_rule_active($test_hr)) {
2031 &write_test_file("[-] new fw rule not timed out.\n",
2032 $current_test_file);
2035 &write_test_file("[+] new fw rule timed out.\n",
2036 $current_test_file);
2037 $fw_rule_removed = 1;
2041 if (&is_fwknopd_running()) {
2043 unless (&file_find_regex([qr/Got\sSIGTERM/, qr/^Terminated/],
2044 $server_test_file)) {
2045 $server_was_stopped = 0;
2048 &write_test_file("[-] server is not running.\n",
2049 $current_test_file);
2050 $server_was_stopped = 0;
2053 return ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed);
2056 sub get_spa_packet_from_file() {
2061 my $found_trigger_line = 0;
2062 open F, "< $file" or die "[*] Could not open file $file: $!";
2064 if (/final\spacked/i) {
2065 $found_trigger_line = 1;
2068 next unless $found_trigger_line;
2070 ### the next line with non whitespace is the SPA packet
2081 sub send_packets() {
2082 my $pkts_ar = shift;
2084 open F, ">> $current_test_file" or die $!;
2085 print F "[+] send_packets(): Sending the following packets...\n";
2086 print F Dumper $pkts_ar;
2089 for my $pkt_hr (@$pkts_ar) {
2090 if ($pkt_hr->{'proto'} eq 'tcp' or $pkt_hr->{'proto'} eq 'udp') {
2091 my $socket = IO::Socket::INET->new(
2092 PeerAddr => $pkt_hr->{'dst_ip'},
2093 PeerPort => $pkt_hr->{'port'},
2094 Proto => $pkt_hr->{'proto'},
2096 ) or die "[*] Could not acquire $pkt_hr->{'proto'}/$pkt_hr->{'port'} " .
2097 "socket to $pkt_hr->{'dst_ip'}: $!";
2099 $socket->send($pkt_hr->{'data'});
2102 } elsif ($pkt_hr->{'proto'} eq 'http') {
2104 } elsif ($pkt_hr->{'proto'} eq 'icmp') {
2108 sleep $pkt_hr->{'delay'} if defined $pkt_hr->{'delay'};
2113 sub generic_exec() {
2114 my $test_hr = shift;
2118 my $exec_rv = &run_cmd($test_hr->{'cmdline'},
2119 $cmd_out_tmp, $current_test_file);
2121 if ($test_hr->{'exec_err'} eq $YES) {
2122 $rv = 0 if $exec_rv;
2124 $rv = 0 unless $exec_rv;
2127 if ($test_hr->{'positive_output_matches'}) {
2128 $rv = 0 unless &file_find_regex(
2129 $test_hr->{'positive_output_matches'},
2130 $current_test_file);
2133 if ($test_hr->{'negative_output_matches'}) {
2134 $rv = 0 if &file_find_regex(
2135 $test_hr->{'negative_output_matches'},
2136 $current_test_file);
2144 my $test_hr = shift;
2145 return 0 unless $test_hr->{'binary'};
2146 &run_cmd("./hardening-check $test_hr->{'binary'}",
2147 $cmd_out_tmp, $current_test_file);
2148 return 0 if &file_find_regex([qr/Position\sIndependent.*:\sno/i],
2149 $current_test_file);
2153 ### check for stack protection
2154 sub stack_protected_binary() {
2155 my $test_hr = shift;
2156 return 0 unless $test_hr->{'binary'};
2157 &run_cmd("./hardening-check $test_hr->{'binary'}",
2158 $cmd_out_tmp, $current_test_file);
2159 return 0 if &file_find_regex([qr/Stack\sprotected.*:\sno/i],
2160 $current_test_file);
2164 ### check for fortified source functions
2165 sub fortify_source_functions() {
2166 my $test_hr = shift;
2167 return 0 unless $test_hr->{'binary'};
2168 &run_cmd("./hardening-check $test_hr->{'binary'}",
2169 $cmd_out_tmp, $current_test_file);
2170 return 0 if &file_find_regex([qr/Fortify\sSource\sfunctions:\sno/i],
2171 $current_test_file);
2175 ### check for read-only relocations
2176 sub read_only_relocations() {
2177 my $test_hr = shift;
2178 return 0 unless $test_hr->{'binary'};
2179 &run_cmd("./hardening-check $test_hr->{'binary'}",
2180 $cmd_out_tmp, $current_test_file);
2181 return 0 if &file_find_regex([qr/Read.only\srelocations:\sno/i],
2182 $current_test_file);
2186 ### check for immediate binding
2187 sub immediate_binding() {
2188 my $test_hr = shift;
2189 return 0 unless $test_hr->{'binary'};
2190 &run_cmd("./hardening-check $test_hr->{'binary'}",
2191 $cmd_out_tmp, $current_test_file);
2192 return 0 if &file_find_regex([qr/Immediate\sbinding:\sno/i],
2193 $current_test_file);
2199 &run_cmd("LD_LIBRARY_PATH=$lib_dir $valgrind_str $fwknopdCmd " .
2200 "$default_server_conf_args --fw-list-all",
2201 $cmd_out_tmp, $current_test_file);
2209 'ls -l /etc', 'if [ -e /etc/issue ]; then cat /etc/issue; fi',
2210 'if [ `which iptables` ]; then iptables -V; fi',
2211 'if [ -e /proc/cpuinfo ]; then cat /proc/cpuinfo; fi',
2212 'if [ -e /proc/config.gz ]; then zcat /proc/config.gz; fi',
2213 'if [ `which gpg` ]; then gpg --version; fi',
2214 'if [ `which tcpdump` ]; then ldd `which tcpdump`; fi',
2218 'ls -l /usr/lib/*pcap*',
2219 'ls -l /usr/local/lib/*pcap*',
2220 'ls -l /usr/lib/*fko*',
2221 'ls -l /usr/local/lib/*fko*',
2223 &run_cmd($cmd, $cmd_out_tmp, $current_test_file);
2225 if ($cmd =~ /^ldd/) {
2226 $have_gpgme++ if &file_find_regex([qr/gpgme/], $cmd_out_tmp);
2230 ### all three of fwknop/fwknopd/libfko must link against gpgme in order
2231 ### to enable gpg tests
2232 unless ($have_gpgme == 3) {
2233 push @tests_to_exclude, "GPG";
2239 sub time_for_valgrind() {
2241 while (&run_cmd("ps axuww | grep LD_LIBRARY_PATH | " .
2242 "grep valgrind |grep -v perl | grep -v grep",
2243 $cmd_out_tmp, $current_test_file)) {
2251 sub anonymize_results() {
2253 die "[*] $output_dir does not exist" unless -d $output_dir;
2254 die "[*] $logfile does not exist, has $0 been executed?"
2257 unlink $tarfile or die "[*] Could not unlink $tarfile: $!";
2260 ### remove non-loopback IP addresses
2261 my $search_re = qr/\b127\.0\.0\.1\b/;
2262 system "perl -p -i -e 's|$search_re|00MY1271STR00|g' $output_dir/*.test";
2263 $search_re = qr/\b127\.0\.0\.2\b/;
2264 system "perl -p -i -e 's|$search_re|00MY1272STR00|g' $output_dir/*.test";
2265 $search_re = qr/\b0\.0\.0\.0\b/;
2266 system "perl -p -i -e 's|$search_re|00MY0000STR00|g' $output_dir/*.test";
2267 $search_re = qr/\b(?:[0-2]?\d{1,2}\.){3}[0-2]?\d{1,2}\b/;
2268 system "perl -p -i -e 's|$search_re|N.N.N.N|g' $output_dir/*.test";
2269 system "perl -p -i -e 's|00MY1271STR00|127.0.0.1|g' $output_dir/*.test";
2270 system "perl -p -i -e 's|00MY1272STR00|127.0.0.2|g' $output_dir/*.test";
2271 system "perl -p -i -e 's|00MY0000STR00|0.0.0.0|g' $output_dir/*.test";
2273 ### remove hostname from any uname output
2274 $search_re = qr/\suname\s+\-a\s*\n\s*(\S+)\s+\S+/;
2275 system "perl -p -i -e 'undef \$/; s|$search_re" .
2276 "| uname -a\n\$1 (removed)|s' $output_dir/*.test";
2278 $search_re = qr/uname=\x27(\S+)\s+\S+/;
2279 system "perl -p -i -e 's|$search_re|uname= \$1 (removed)|' $output_dir/*.test";
2282 system "tar cvfz $tarfile $logfile $output_dir";
2283 print "[+] Anonymized test results file: $tarfile\n";
2292 my $test_hr = shift;
2294 open F, "> $default_pid_file" or die $!;
2298 &server_start($test_hr);
2300 open F, "< $default_pid_file" or die $!;
2312 sub start_fwknopd() {
2313 my $test_hr = shift;
2315 &write_test_file("[+] TEST: $test_hr->{'msg'}\n", $server_test_file);
2318 die "[*] Could not fork: $!" unless defined $pid;
2322 ### we are the child, so start fwknopd
2323 exit &run_cmd($test_hr->{'fwknopd_cmdline'},
2324 $server_cmd_tmp, $server_test_file);
2330 my ($key, $file) = @_;
2332 open K, "> $file" or die "[*] Could not open $file: $!";
2333 print K "$loopback_ip: $key\n";
2334 print K "localhost: $key\n";
2335 print K "some.host.through.proxy.com: $key\n";
2341 open C, ">> $current_test_file"
2342 or die "[*] Could not open $current_test_file: $!";
2343 print C "\n" . localtime() . " [+] PID dump:\n";
2345 &run_cmd("ps auxww | grep knop |grep -v grep",
2346 $cmd_out_tmp, $current_test_file);
2351 my ($cmd, $cmd_out, $file) = @_;
2355 or die "[*] Could not open $file: $!";
2356 print F localtime() . " CMD: $cmd\n";
2360 or die "[*] Could not open $file: $!";
2361 print F localtime() . " CMD: $cmd\n";
2365 my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
2367 open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
2368 my @cmd_lines = <C>;
2371 open F, ">> $file" or die "[*] Could not open $file: $!";
2372 print F $_ for @cmd_lines;
2385 for (my $i=length($msg); $i < $PRINT_LEN; $i++) {
2394 $|++; ### turn off buffering
2396 $< == 0 && $> == 0 or
2397 die "[*] $0: You must be root (or equivalent ",
2398 "UID 0 account) to effectively test fwknop";
2400 ### validate test hashes
2402 for my $test_hr (@tests) {
2403 for my $key (keys %test_keys) {
2404 if ($test_keys{$key} == $REQUIRED) {
2405 die "[*] Missing '$key' element in hash: $hash_num"
2406 unless defined $test_hr->{$key};
2408 $test_hr->{$key} = '' unless defined $test_hr->{$key};
2414 if ($use_valgrind) {
2415 die "[*] $valgrindCmd exec problem, use --valgrind-path"
2416 unless -e $valgrindCmd and -x $valgrindCmd;
2419 die "[*] $conf_dir directory does not exist." unless -d $conf_dir;
2420 die "[*] $lib_dir directory does not exist." unless -d $lib_dir;
2422 for my $file ($configure_path,
2425 $default_access_conf,
2426 $no_source_match_access_conf,
2427 $ip_source_match_access_conf,
2428 $subnet_source_match_access_conf,
2429 $no_subnet_source_match_access_conf,
2430 $no_multi_source_match_access_conf,
2431 $multi_source_match_access_conf,
2432 $open_ports_access_conf,
2433 $mismatch_open_ports_access_conf,
2434 $require_user_access_conf,
2435 $mismatch_user_access_conf,
2436 $require_src_access_conf,
2437 $multi_gpg_access_conf,
2438 $multi_stanzas_access_conf,
2439 $expired_access_conf,
2440 $expired_epoch_access_conf,
2441 $future_expired_access_conf,
2442 $invalid_expire_access_conf,
2443 $force_nat_access_conf,
2445 die "[*] $file does not exist" unless -e $file;
2448 if (-d $output_dir) {
2449 if (-d "${output_dir}.last") {
2450 rmtree "${output_dir}.last"
2451 or die "[*] rmtree ${output_dir}.last $!";
2453 mkdir "${output_dir}.last"
2454 or die "[*] ${output_dir}.last: $!";
2455 for my $file (glob("$output_dir/*.test")) {
2456 if ($file =~ m|.*/(.*)|) {
2457 copy $file, "${output_dir}.last/$1" or die $!;
2460 if (-e "$output_dir/init") {
2461 copy "$output_dir/init", "${output_dir}.last/init";
2464 copy $logfile, "${output_dir}.last/$logfile" or die $!;
2466 $saved_last_results = 1;
2468 mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
2470 unless (-d $run_dir) {
2471 mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
2474 for my $file (glob("$output_dir/*.test")) {
2475 unlink $file or die "[*] Could not unlink($file)";
2477 if (-e "$output_dir/init") {
2478 unlink "$output_dir/init" or die $!;
2482 unlink $logfile or die $!;
2485 if ($test_include) {
2486 @tests_to_include = split /\s*,\s*/, $test_include;
2488 if ($test_exclude) {
2489 @tests_to_exclude = split /\s*,\s*/, $test_exclude;
2492 ### make sure no fwknopd instance is currently running
2493 die "[*] Please stop the running fwknopd instance."
2494 if &is_fwknopd_running();
2496 unless ($enable_recompilation_warnings_check) {
2497 push @tests_to_exclude, 'recompilation';
2500 $sudo_path = &find_command('sudo');
2502 unless ((&find_command('cc') or &find_command('gcc')) and &find_command('make')) {
2503 ### disable compilation checks
2504 push @tests_to_exclude, 'recompilation';
2507 open UNAME, "uname |" or die "[*] Could not execute uname: $!";
2510 $platform = 'linux';
2516 unless ($platform eq 'linux') {
2517 push @tests_to_exclude, 'NAT';
2523 sub identify_loopback_intf() {
2524 return if $loopback_intf;
2528 ### lo Link encap:Local Loopback
2529 ### inet addr:127.0.0.1 Mask:255.0.0.0
2530 ### inet6 addr: ::1/128 Scope:Host
2531 ### UP LOOPBACK RUNNING MTU:16436 Metric:1
2532 ### RX packets:534709 errors:0 dropped:0 overruns:0 frame:0
2533 ### TX packets:534709 errors:0 dropped:0 overruns:0 carrier:0
2534 ### collisions:0 txqueuelen:0
2535 ### RX bytes:101110617 (101.1 MB) TX bytes:101110617 (101.1 MB)
2539 ### lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
2540 ### options=3<RXCSUM,TXCSUM>
2541 ### inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
2542 ### inet6 ::1 prefixlen 128
2543 ### inet 127.0.0.1 netmask 0xff000000
2544 ### nd6 options=3<PERFORMNUD,ACCEPT_RTADV>
2547 my $found_loopback_intf = 0;
2549 my $cmd = 'ifconfig -a';
2550 open C, "$cmd |" or die "[*] (use --loopback <name>) $cmd: $!";
2552 if (/^(\S+?):?\s+.*loopback/i) {
2556 if (/^\S/ and $intf and not $found_loopback_intf) {
2557 ### should not happen
2560 if ($intf and /\b127\.0\.0\.1\b/) {
2561 $found_loopback_intf = 1;
2567 die "[*] could not determine loopback interface, use --loopback <name>"
2568 unless $found_loopback_intf;
2570 $loopback_intf = $intf;
2575 sub is_fw_rule_active() {
2576 my $test_hr = shift;
2578 my $conf_args = $default_server_conf_args;
2580 if ($test_hr->{'server_conf'}) {
2581 $conf_args = "-c $test_hr->{'server_conf'} -a $default_access_conf " .
2582 "-d $default_digest_file -p $default_pid_file";
2585 return 1 if &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2586 qq{$conf_args --fw-list | grep -v "# DISABLED" |grep $fake_ip |grep _exp_},
2587 $cmd_out_tmp, $current_test_file);
2591 sub is_fwknopd_running() {
2593 sleep 2 if $use_valgrind;
2595 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd $default_server_conf_args " .
2596 "--status", $cmd_out_tmp, $current_test_file);
2598 return 0 if &file_find_regex([qr/no\s+running/i], $cmd_out_tmp);
2603 sub stop_fwknopd() {
2605 &run_cmd("LD_LIBRARY_PATH=$lib_dir $fwknopdCmd " .
2606 "$default_server_conf_args -K", $cmd_out_tmp, $current_test_file);
2608 if ($use_valgrind) {
2609 &time_for_valgrind();
2617 sub file_find_regex() {
2618 my ($re_ar, $file) = @_;
2621 my @write_lines = ();
2623 open F, "< $file" or die "[*] Could not open $file: $!";
2626 next LINE if $line =~ /file_file_regex\(\)/;
2627 for my $re (@$re_ar) {
2629 push @write_lines, "[.] file_find_regex() " .
2630 "Matched '$re' with line: $line";
2639 for my $line (@write_lines) {
2640 &write_test_file($line, $file);
2643 &write_test_file("[.] find_find_regex() Did not " .
2644 "match any regex in: '@$re_ar'\n", $file);
2650 sub find_command() {
2654 open C, "which $cmd |" or die "[*] Could not execute: which $cmd: $!";
2656 if (m|^(/.*$cmd)$|) {
2665 sub write_test_file() {
2666 my ($msg, $file) = @_;
2670 or die "[*] Could not open $file: $!";
2675 or die "[*] Could not open $file: $!";
2685 open F, ">> $logfile" or die $!;