diff --git a/ChangeLog b/ChangeLog index eaa4ea24..bd445900 100644 --- a/ChangeLog +++ b/ChangeLog @@ -58,6 +58,9 @@ fwknop-2.0.4 (11//2012): - [test suite] For GnuPG tests that require a passphrase associated with a gpg key, added a pinentry check to see if the local gpg engine requires it. If so, the gpg test that require a key are excluded since. + - [server] Added a new '--pcap-file ' option to allow pcap files to + be processed directly by fwknopd instead of sniffing an interface. This + feature is mostly intended for debugging purposes. fwknop-2.0.3 (09/03/2012): - [server] Fernando Arnaboldi from IOActive found several DoS/code diff --git a/Makefile.am b/Makefile.am index 7b9966ad..3b890226 100644 --- a/Makefile.am +++ b/Makefile.am @@ -160,6 +160,7 @@ EXTRA_DIST = \ test/conf/tcp_pcap_filter_fwknopd.conf \ test/conf/icmp_pcap_filter_fwknopd.conf \ test/conf/tcp_server_fwknopd.conf \ + test/conf/spa_replay.pcap \ test/fuzzing/patches/enable_perl_fko_bogus_packets.patch \ test/fuzzing/patches/encoding_append_b64_modified_byte \ test/fuzzing/patches/encoding_append_b64_modified_byte_eq.patch \ diff --git a/doc/fwknopd.man.asciidoc b/doc/fwknopd.man.asciidoc index 19eb8afe..b951dfc5 100644 --- a/doc/fwknopd.man.asciidoc +++ b/doc/fwknopd.man.asciidoc @@ -102,6 +102,12 @@ COMMAND-LINE OPTIONS line. This overrides the value of the PCAP_FILTER variable taken from the 'fwknopd.conf' file. +*--pcap-file*='':: + This option instructs *fwknopd* to read packet data from a pcap file + instead of sniffing an interface directly. This mode is usually used for + debugging purposes, and will disable SPA packet age checking unless it is + manually enabled in the 'fwknop.conf' file. + *-R, --Restart*:: Restart the currently running *fwknopd* processes. This option will preserve the command line options that were supplied to the diff --git a/server/cmd_opts.h b/server/cmd_opts.h index 0abe85d1..e806d7da 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -42,6 +42,7 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = { "OVERRIDE_CONFIG", //"FIREWALL_TYPE", "PCAP_INTF", + "PCAP_FILE", "ENABLE_PCAP_PROMISC", "PCAP_FILTER", "PCAP_DISPATCH_COUNT", @@ -115,6 +116,7 @@ enum { FW_LIST_ALL, FW_FLUSH, GPG_HOME_DIR, + PCAP_FILE, ROTATE_DIGEST_CACHE, NOOP /* Just to be a marker for the end */ }; @@ -143,6 +145,7 @@ static struct option cmd_opts[] = {"locale", 1, NULL, 'l' }, {"rotate-digest-cache", 0, NULL, ROTATE_DIGEST_CACHE }, {"override-config", 1, NULL, 'O' }, + {"pcap-file", 1, NULL, PCAP_FILE }, {"pcap-filter", 1, NULL, 'P'}, {"pid-file", 1, NULL, 'p'}, {"restart", 0, NULL, 'R'}, diff --git a/server/config_init.c b/server/config_init.c index a728cdae..a2180660 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -337,7 +337,8 @@ validate_options(fko_srv_options_t *opts) /* Set remaining require CONF_ vars if they are not already set. */ - /* PCAP capture interface. + /* PCAP capture interface - note that if '-r ' is specified + * on the command line, then this will override the pcap interface setting. */ if(opts->config[CONF_PCAP_INTF] == NULL) set_config_entry(opts, CONF_PCAP_INTF, DEF_INTERFACE); @@ -365,11 +366,21 @@ validate_options(fko_srv_options_t *opts) if(opts->config[CONF_PCAP_FILTER] == NULL) set_config_entry(opts, CONF_PCAP_FILTER, DEF_PCAP_FILTER); - /* Enable SPA packet aging. + /* Enable SPA packet aging unless we're getting packet data + * directly from a pcap file */ if(opts->config[CONF_ENABLE_SPA_PACKET_AGING] == NULL) - set_config_entry(opts, CONF_ENABLE_SPA_PACKET_AGING, - DEF_ENABLE_SPA_PACKET_AGING); + { + if(opts->config[CONF_PCAP_FILE] == NULL) + { + set_config_entry(opts, CONF_ENABLE_SPA_PACKET_AGING, + DEF_ENABLE_SPA_PACKET_AGING); + } + else + { + set_config_entry(opts, CONF_ENABLE_SPA_PACKET_AGING, "N"); + } + } /* SPA packet age. */ @@ -798,6 +809,9 @@ config_init(fko_srv_options_t *opts, int argc, char **argv) case 'P': set_config_entry(opts, CONF_PCAP_FILTER, optarg); break; + case PCAP_FILE: + set_config_entry(opts, CONF_PCAP_FILE, optarg); + break; case ROTATE_DIGEST_CACHE: opts->rotate_digest_cache = 1; break; diff --git a/server/fwknopd.conf b/server/fwknopd.conf index c8afc085..c23c5a5b 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -27,7 +27,8 @@ # # Define the ethernet interface on which we will sniff packets. -# Default if not set is eth0. +# Default if not set is eth0. The '-i ' command line option overrides +# the PCAP_INTF setting. # #PCAP_INTF eth0; @@ -63,13 +64,13 @@ # #ENABLE_DIGEST_PERSISTENCE Y; -# Sets the number of packets that are processed when the *pcap_dispatch()* -# call is made. The default is zero, since this allows *fwknopd* to process +# Sets the number of packets that are processed when the pcap_dispatch() +# call is made. The default is zero, since this allows fwknopd to process # as many packets as possible in the corresponding callback where the SPA # handling routine is called for packets that pass a set of prerequisite -# checks. However, if *fwknopd* is running on a platform with an old +# checks. However, if fwknopd is running on a platform with an old # version of libpcap, it may be necessary to change this value to a positive -# non-zero integer. More information can be found in the *pcap_dispatch(3)* +# non-zero integer. More information can be found in the pcap_dispatch(3) # man page. #PCAP_DISPATCH_COUNT 0; @@ -121,6 +122,12 @@ #SYSLOG_IDENTITY fwknopd; #SYSLOG_FACILITY LOG_DAEMON; +# Define this to have fwknopd read pcap data from a file instead of sniffing +# a live interface. This is usually only used for debugging purposes, and is +# equivalent to the '-r ' command line option. +# +#PCAP_FILE /some/path/to/file.pcap; + ############################################################################## # NOTE: The following EXTERNAL_CMD functionality is not yet implemented. # This is a possible future feature of fwknopd. diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index cd4d42e9..10c94503 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -165,6 +165,7 @@ enum { CONF_OVERRIDE_CONFIG, //CONF_FIREWALL_TYPE, CONF_PCAP_INTF, + CONF_PCAP_FILE, CONF_ENABLE_PCAP_PROMISC, CONF_PCAP_FILTER, CONF_PCAP_DISPATCH_COUNT, diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 67929c20..606688d6 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -254,7 +254,7 @@ incoming_spa(fko_srv_options_t *opts) char *spa_ip_demark, *gpg_id, *raw_digest = NULL; time_t now_ts; int res, status, ts_diff, enc_type, stanza_num=0; - int added_replay_digest = 0; + int added_replay_digest = 0, pkt_data_len=0; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); @@ -273,6 +273,7 @@ incoming_spa(fko_srv_options_t *opts) * SPA data and/or to be reasonably sure we have a SPA packet (i.e * try to eliminate obvious non-spa packets). */ + pkt_data_len = spa_pkt->packet_data_len; res = preprocess_spa_data(opts, spadat.pkt_source_ip); if(res != FKO_SUCCESS) { @@ -282,6 +283,12 @@ incoming_spa(fko_srv_options_t *opts) return; } + if(opts->foreground == 1 && opts->verbose > 2) + { + printf("[+] candidate SPA packet payload:\n"); + hex_dump(spa_pkt->packet_data, pkt_data_len); + } + if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip))) { if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) diff --git a/server/pcap_capture.c b/server/pcap_capture.c index 99d63109..47be8605 100644 --- a/server/pcap_capture.c +++ b/server/pcap_capture.c @@ -57,6 +57,7 @@ pcap_capture(fko_srv_options_t *opts) int pending_break = 0; int promisc = 0; int set_direction = 1; + int pcap_file_mode = 0; int status; int useconds; pid_t child_pid; @@ -72,25 +73,39 @@ pcap_capture(fko_srv_options_t *opts) if(opts->config[CONF_ENABLE_PCAP_PROMISC][0] == 'Y') promisc = 1; - log_msg(LOG_INFO, "Sniffing interface: %s", - opts->config[CONF_PCAP_INTF]); + if(opts->config[CONF_PCAP_FILE] != NULL + && opts->config[CONF_PCAP_FILE][0] != '\0') + pcap_file_mode = 1; - pcap = pcap_open_live( - opts->config[CONF_PCAP_INTF], - atoi(opts->config[CONF_MAX_SNIFF_BYTES]), - promisc, 100, errstr - ); + if(pcap_file_mode == 1) { + log_msg(LOG_INFO, "Reading pcap file: %s", + opts->config[CONF_PCAP_FILE]); - if(pcap == NULL) - { - log_msg(LOG_ERR, "[*] pcap_open_live error: %s\n", errstr); - clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + pcap = pcap_open_offline(opts->config[CONF_PCAP_FILE], errstr); + + if(pcap == NULL) + { + log_msg(LOG_ERR, "[*] pcap_open_offline() error: %s\n", + errstr); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } } - - if (pcap == NULL) + else { - log_msg(LOG_ERR, "[*] pcap error: %s", errstr); - clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + log_msg(LOG_INFO, "Sniffing interface: %s", + opts->config[CONF_PCAP_INTF]); + + pcap = pcap_open_live( + opts->config[CONF_PCAP_INTF], + atoi(opts->config[CONF_MAX_SNIFF_BYTES]), + promisc, 100, errstr + ); + + if(pcap == NULL) + { + log_msg(LOG_ERR, "[*] pcap_open_live() error: %s\n", errstr); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } } /* Set pcap filters, if any. @@ -144,7 +159,8 @@ pcap_capture(fko_srv_options_t *opts) /* We are only interested on seeing packets coming into the interface. */ - if (set_direction && (pcap_setdirection(pcap, PCAP_D_IN) < 0)) + if (set_direction && (pcap_file_mode == 0) + && (pcap_setdirection(pcap, PCAP_D_IN) < 0)) if(opts->verbose) log_msg(LOG_WARNING, "[*] Warning: pcap error on setdirection: %s.", pcap_geterr(pcap)); @@ -153,7 +169,7 @@ pcap_capture(fko_srv_options_t *opts) * * NOTE: This is simply set to 0 for now until we find a need * to actually use this mode (which when set on a FreeBSD - * system, it silently breaks the packet capture). + * system, it silently breaks the packet capture). */ if((pcap_setnonblock(pcap, DEF_PCAP_NONBLOCK, errstr)) == -1) { @@ -233,6 +249,9 @@ pcap_capture(fko_srv_options_t *opts) */ if(res > 0) { + if(opts->foreground == 1 && opts->verbose > 2) + log_msg(LOG_INFO, "pcap_dispatch() processed: %d packets", res); + /* Count the set of processed packets (pcap_dispatch() return * value) - we use this as a comparison for --packet-limit regardless * of SPA packet validity at this point. diff --git a/test/conf/spa_replay.pcap b/test/conf/spa_replay.pcap new file mode 100644 index 00000000..4c963e94 Binary files /dev/null and b/test/conf/spa_replay.pcap differ diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 68b266e2..b7dedada 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -20,6 +20,7 @@ my $cmd_out_tmp = 'cmd.out'; my $server_cmd_tmp = 'server_cmd.out'; my $gpg_client_home_dir = "$conf_dir/client-gpg"; my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw"; +my $replay_pcap_file = "$conf_dir/spa_replay.pcap"; my %cf = ( 'nat' => "$conf_dir/nat_fwknopd.conf", @@ -129,6 +130,7 @@ my $NO = 0; my $PRINT_LEN = 68; my $USE_PREDEF_PKTS = 1; my $USE_CLIENT = 2; +my $USE_PCAP_FILE = 3; my $REQUIRED = 1; my $OPTIONAL = 0; my $NEW_RULE_REQUIRED = 1; @@ -1189,6 +1191,25 @@ my @tests = ( 'fatal' => $NO }, + ### --pcap-file + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => '--pcap-file processing', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&process_pcap_file_directly, + 'cmdline' => '', + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args " . + "--pcap-file $replay_pcap_file --foreground --verbose --verbose " . + "--verbose", + 'server_positive_output_matches' => [qr/Replay\sdetected/i, + qr/candidate\sSPA/, qr/0x0000\:\s+2b/], + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { 'category' => 'Rijndael SPA', 'subcategory' => 'client+server', @@ -4243,6 +4264,46 @@ sub altered_non_base64_spa_data() { return $rv; } +sub process_pcap_file_directly() { + my $test_hr = shift; + + my $rv = 1; + my $server_was_stopped = 0; + my $fw_rule_created = 0; + my $fw_rule_removed = 0; + + ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed) + = &client_server_interaction($test_hr, [], $USE_PCAP_FILE); + + $rv = 0 unless $server_was_stopped; + + if ($test_hr->{'fw_rule_created'} eq $NEW_RULE_REQUIRED) { + $rv = 0 unless $fw_rule_created; + } elsif ($test_hr->{'fw_rule_created'} eq $REQUIRE_NO_NEW_RULE) { + $rv = 0 if $fw_rule_created; + } + + if ($test_hr->{'fw_rule_removed'} eq $NEW_RULE_REMOVED) { + $rv = 0 unless $fw_rule_removed; + } elsif ($test_hr->{'fw_rule_removed'} eq $REQUIRE_NO_NEW_REMOVED) { + $rv = 0 if $fw_rule_removed; + } + + if ($test_hr->{'server_positive_output_matches'}) { + $rv = 0 unless &file_find_regex( + $test_hr->{'server_positive_output_matches'}, + $MATCH_ALL, $server_test_file); + } + + if ($test_hr->{'server_negative_output_matches'}) { + $rv = 0 if &file_find_regex( + $test_hr->{'server_negative_output_matches'}, + $MATCH_ANY, $server_test_file); + } + + return $rv; +} + sub fuzzer() { my $test_hr = shift; @@ -4552,8 +4613,10 @@ sub client_server_interaction() { $current_test_file); $rv = 0; } - } else { + } elsif ($spa_client_flag == $USE_PREDEF_PKTS) { &send_packets($pkts_hr); + } else { + ### pcap file mode, nothing to do } ### check to see if the SPA packet resulted in a new fw access rule diff --git a/todo.org b/todo.org index e171a5ee..92525d62 100644 --- a/todo.org +++ b/todo.org @@ -2,6 +2,11 @@ This is the main todo org mode file for the fwknop project ** COMPLETED This bucket is for completed tasks. +*** [server] Add the ability to process pcap files offline + :CLOSED: <2012-11-08 Thu> + Leverage pcap_open_offline() to process pcap files from disk instead of + sniffing the network live. + - Added a new '--pcap-file ' option for this purpose. *** Add --disable-gpg arg to the autoconf configure script :CLOSED: <2012-10-31 Wed> There needs to be a way to easily disable libgpgme usage even if it is @@ -48,9 +53,6 @@ :CLOSED: <2012-10-02 Tue> The access.c parsing code currently throws an error if there is not KEY variable in an access stanza even if GPG_ALLOW_NO_PW is set. -** [server] Add the ability to process pcap files offline - Leverage pcap_open_offline() to process pcap files from disk instead of - sniffing the network live. ** [server] Add PF NAT support for OpenBSD systems fwknopd already supports various NAT modes on iptables, but it should be extended to support NAT on PF firewalls.