From 624872ef4889cb43f5ecb42b74d9722bd6e9a4a8 Mon Sep 17 00:00:00 2001 From: Grant Pannell Date: Sat, 29 Nov 2014 15:05:06 +1030 Subject: [PATCH 1/9] Add DESTINATION access.conf directive and ENABLE_DESTINATION_RULE fwknopd.conf directive --- Makefile.am | 1 + doc/fwknopd.man.asciidoc | 18 ++++++++ server/access.c | 51 ++++++++++++++++------ server/access.conf | 13 ++++++ server/cmd_opts.h | 1 + server/config_init.c | 6 +++ server/fw_util_firewalld.c | 58 ++++++++++++++++--------- server/fw_util_firewalld.h | 7 +-- server/fw_util_ipf.c | 6 ++- server/fw_util_ipf.h | 1 + server/fw_util_ipfw.c | 6 +++ server/fw_util_ipfw.h | 3 +- server/fw_util_iptables.c | 58 ++++++++++++++++--------- server/fw_util_iptables.h | 7 +-- server/fw_util_pf.c | 6 +++ server/fw_util_pf.h | 5 ++- server/fwknopd.8.in | 12 +++++ server/fwknopd.conf | 10 +++++ server/fwknopd_common.h | 13 ++++++ server/incoming_spa.c | 8 +++- test/conf/destination_rule_fwknopd.conf | 1 + test/tests/basic_operations.pl | 16 +++++++ 22 files changed, 242 insertions(+), 65 deletions(-) create mode 100644 test/conf/destination_rule_fwknopd.conf diff --git a/Makefile.am b/Makefile.am index 251b17c2..d9cda93e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -314,6 +314,7 @@ EXTRA_DIST = \ test/conf/firewd_snat_fwknopd.conf \ test/conf/ipt_snat_no_translate_ip_fwknopd.conf \ test/conf/firewd_snat_no_translate_ip_fwknopd.conf \ + test/conf/destination_rule_fwknopd.conf \ test/conf/spa_replay.pcap \ test/conf/fcs_spa.pcap \ test/fko-wrapper/Makefile \ diff --git a/doc/fwknopd.man.asciidoc b/doc/fwknopd.man.asciidoc index 6a503b50..e0af2a2d 100644 --- a/doc/fwknopd.man.asciidoc +++ b/doc/fwknopd.man.asciidoc @@ -384,6 +384,15 @@ See the '@sysconfdir@/fwknop/fwknopd.conf' file for the full list and correspond *SYSLOG_FACILITY* '':: Override syslog facility. The ``SYSLOG_FACILITY'' variable can be set to one of ``LOG_LOCAL{0-7}'' or ``LOG_DAEMON'' (the default). + +*ENABLE_DESTINATION_RULE* '':: + Controls whether *fwknopd* will set the destination field on the firewall + rule to the destination address specified on the incoming SPA packet. + This is useful for interfaces with multiple IP addresses hosting separate + services. If ``ENABLE_IPT_OUTPUT'' is set to ``Y'', the source field of + the firewall rule is set. FORWARD and SNAT rules are not affected however, + DNAT rules will also have their destination field set. The default is + ``N'', which sets the destination field to 0.0.0.0/0 (any). *FWKNOP_RUN_DIR* '':: Specify the directory where *fwknopd* writes run time state files. The @@ -411,6 +420,15 @@ directive starts a new stanza. and individual IP addresses can be specified as well. Also, multiple IP's and/or networks can be defined as a comma separated list (e.g. ``192.168.10.0/24,10.1.1.123'') + +*DESTINATION* '':: + This defines the destination address which the SPA packet will be + accepted. The string ``ANY'' is also accepted if a valid SPA packet + should be honored to any destination IP. + Networks should be specified in CIDR notation (e.g. ``192.168.10.0/24''), + and individual IP addresses can be specified as well. Also, multiple + IP's and/or networks can be defined as a comma separated list (e.g. + ``192.168.10.0/24,10.1.1.123'') *OPEN_PORTS* ',...,':: Define a set of ports and protocols (tcp or udp) that will be diff --git a/server/access.c b/server/access.c index eb8c9df2..a7ae679a 100644 --- a/server/access.c +++ b/server/access.c @@ -229,7 +229,7 @@ add_acc_force_snat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char * * comparisons of incoming source IPs against this mask. */ static int -add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip) +add_int_ent(acc_int_list_t **ilist, const char *ip) { char *ndx; char ip_str[MAX_IPV4_STR_LEN] = {0}; @@ -247,7 +247,7 @@ add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip) log_msg(LOG_ERR, "[*] Fatal memory allocation error adding stanza source_list entry" ); - clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + exit(EXIT_FAILURE); } /* Convert the IP data into the appropriate IP + (optional) mask @@ -370,13 +370,13 @@ add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip) /* If this is not the first entry, we walk our pointer to the * end of the list. */ - if(acc->source_list == NULL) + if(*ilist == NULL) { - acc->source_list = new_sle; + *ilist = new_sle; } else { - tmp_sle = acc->source_list; + tmp_sle = *ilist; do { last_sle = tmp_sle; @@ -391,13 +391,13 @@ add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip) /* Expand the access SOURCE string to a list of masks. */ static int -expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc) +expand_acc_int_list(acc_int_list_t **ilist, char *ip) { char *ndx, *start; char buf[ACCESS_BUF_LEN] = {0}; int res = 1; - start = acc->source; + start = ip; for(ndx = start; *ndx; ndx++) { @@ -413,7 +413,7 @@ expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc) strlcpy(buf, start, (ndx-start)+1); - res = add_source_mask(opts, acc, buf); + res = add_int_ent(ilist, buf); if(res == 0) return res; @@ -431,7 +431,7 @@ expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc) strlcpy(buf, start, (ndx-start)+1); - res = add_source_mask(opts, acc, buf); + res = add_int_ent(ilist, buf); return res; } @@ -504,7 +504,7 @@ add_port_list_ent(acc_port_list_t **plist, char *port_str) if((new_plist = calloc(1, sizeof(acc_port_list_t))) == NULL) { log_msg(LOG_ERR, - "[*] Fatal memory allocation error adding stanza source_list entry" + "[*] Fatal memory allocation error adding stanza port_list entry" ); exit(EXIT_FAILURE); } @@ -677,7 +677,7 @@ expand_acc_string_list(acc_string_list_t **stlist, char *stlist_str) /* Free the acc source_list */ static void -free_acc_source_list(acc_int_list_t *sle) +free_acc_int_list(acc_int_list_t *sle) { acc_int_list_t *last_sle; @@ -747,7 +747,13 @@ free_acc_stanza_data(acc_stanza_t *acc) if(acc->source != NULL) { free(acc->source); - free_acc_source_list(acc->source_list); + free_acc_int_list(acc->source_list); + } + + if(acc->destination != NULL) + { + free(acc->destination); + free_acc_int_list(acc->destination_list); } if(acc->open_ports != NULL) @@ -839,11 +845,20 @@ expand_acc_ent_lists(fko_srv_options_t *opts) { /* Expand the source string to 32-bit integer IP + masks for each entry. */ - if(expand_acc_source(opts, acc) == 0) + if(expand_acc_int_list(&(acc->source_list), acc->source) == 0) { log_msg(LOG_ERR, "[*] Fatal invalid SOURCE in access stanza"); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } + + if(acc->destination != NULL && strlen(acc->destination)) + { + if(expand_acc_int_list(&(acc->destination_list), acc->destination) == 0) + { + log_msg(LOG_ERR, "[*] Fatal invalid DESTINATION in access stanza"); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + } /* Now expand the open_ports string. */ @@ -1290,6 +1305,14 @@ parse_access_file(fko_srv_options_t *opts) */ continue; } + else if(CONF_VAR_IS(var, "DESTINATION")) + { + if(add_acc_string(&(curr_acc->destination), val) != SUCCESS) + { + fclose(file_ptr); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + } else if(CONF_VAR_IS(var, "OPEN_PORTS")) { if(add_acc_string(&(curr_acc->open_ports), val) != SUCCESS) @@ -1898,6 +1921,7 @@ dump_access_list(const fko_srv_options_t *opts) fprintf(stdout, "SOURCE (%i): %s\n" "==============================================================\n" + " DESTINATION: %s\n" " OPEN_PORTS: %s\n" " RESTRICT_PORTS: %s\n" " KEY: %s\n" @@ -1928,6 +1952,7 @@ dump_access_list(const fko_srv_options_t *opts) " GPG_FINGERPRINT_ID: %s\n", ++i, acc->source, + (acc->destination == NULL) ? "" : acc->destination, (acc->open_ports == NULL) ? "" : acc->open_ports, (acc->restrict_ports == NULL) ? "" : acc->restrict_ports, (acc->key == NULL) ? "" : "", diff --git a/server/access.conf b/server/access.conf index f61ad872..22e90c96 100644 --- a/server/access.conf +++ b/server/access.conf @@ -36,6 +36,19 @@ # be honored from any source IP. # +# DESTINATION +# +# This defines the destination address from which a SPA packet will be accepted. +# Networks should be specified in CIDR (e.g. "192.168.10.0/24") notation. +# Individual IP addresses can be specified as well. +# +# Also, multiple IP’s and/or networks can be defined as a comma-separated +# list (e.g. "192.168.10.0/24,10.1.1.123"). +# +# The string "ANY" is also accepted if a valid authorization packet should +# be honored to any destination IP. +# + # OPEN_PORTS , ..., config[CONF_ENABLE_DIGEST_PERSISTENCE] == NULL) set_config_entry(opts, CONF_ENABLE_DIGEST_PERSISTENCE, DEF_ENABLE_DIGEST_PERSISTENCE); + + /* Enable destination rule. + */ + if(opts->config[CONF_ENABLE_DESTINATION_RULE] == NULL) + set_config_entry(opts, CONF_ENABLE_DESTINATION_RULE, + DEF_ENABLE_DESTINATION_RULE); /* Max sniff bytes. */ diff --git a/server/fw_util_firewalld.c b/server/fw_util_firewalld.c index 908a4dd0..5fb054b9 100644 --- a/server/fw_util_firewalld.c +++ b/server/fw_util_firewalld.c @@ -70,14 +70,15 @@ chop_newline(char *str) static int rule_exists_no_chk_support(const fko_srv_options_t * const opts, const struct fw_chain * const fwc, const unsigned int proto, - const char * const ip, const unsigned int port, - const unsigned int exp_ts) + const char * const srcip, const char * const dstip, + const unsigned int port, const unsigned int exp_ts) { int rule_exists=0, rule_num=0, rtmp=0; char cmd_buf[CMD_BUFSIZE] = {0}; char target_search[CMD_BUFSIZE] = {0}; char proto_search[CMD_BUFSIZE] = {0}; - char ip_search[CMD_BUFSIZE] = {0}; + char srcip_search[CMD_BUFSIZE] = {0}; + char dstip_search[CMD_BUFSIZE] = {0}; char port_search[CMD_BUFSIZE] = {0}; char exp_ts_search[CMD_BUFSIZE] = {0}; @@ -98,7 +99,11 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, snprintf(port_search, CMD_BUFSIZE-1, ":%u ", port); snprintf(target_search, CMD_BUFSIZE-1, " %s ", fwc->target); - snprintf(ip_search, CMD_BUFSIZE-1, " %s ", ip); + snprintf(srcip_search, CMD_BUFSIZE-1, " %s ", srcip); + if (dstip != NULL) + { + snprintf(dstip_search, CMD_BUFSIZE-1, " %s ", dstip); + } snprintf(exp_ts_search, CMD_BUFSIZE-1, "%u ", exp_ts); /* search for each of the substrings, and require the returned @@ -114,25 +119,28 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, NO_TIMEOUT, proto_search, &pid_status, opts); if(rtmp == rule_num) rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, ip_search, &pid_status, opts); + NO_TIMEOUT, srcip_search, &pid_status, opts); if(rtmp == rule_num) - rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, target_search, &pid_status, opts); + rtmp = (dstip == NULL) ? rtmp : search_extcmd(cmd_buf, WANT_STDERR, + NO_TIMEOUT, dstip_search, &pid_status, opts); if(rtmp == rule_num) rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, port_search, &pid_status, opts); + NO_TIMEOUT, target_search, &pid_status, opts); if(rtmp == rule_num) - rule_exists = 1; + rtmp = search_extcmd(cmd_buf, WANT_STDERR, + NO_TIMEOUT, port_search, &pid_status, opts); + if(rtmp == rule_num) + rule_exists = 1; } if(rule_exists) log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule (already exists", - proto_search, port, ip, exp_ts); + proto_search, port, srcip, exp_ts); else log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist", - proto_search, port, ip, exp_ts); + proto_search, port, srcip, exp_ts); return(rule_exists); } @@ -174,15 +182,16 @@ rule_exists_chk_support(const fko_srv_options_t * const opts, static int rule_exists(const fko_srv_options_t * const opts, const struct fw_chain * const fwc, const char * const rule, - const unsigned int proto, const char * const ip, - const unsigned int port, const unsigned int exp_ts) + const unsigned int proto, const char * const srcip, + const char * const dstip, const unsigned int port, + const unsigned int exp_ts) { int rule_exists = 0; if(have_firewd_chk_support == 1) rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule); else - rule_exists = rule_exists_no_chk_support(opts, fwc, proto, ip, port, exp_ts); + rule_exists = rule_exists_no_chk_support(opts, fwc, proto, srcip, (opts->fw_config->use_destination ? dstip : NULL), port, exp_ts); if(rule_exists == 1) log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists", @@ -857,6 +866,11 @@ fw_config_init(fko_srv_options_t * const opts) } } } + + if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0) + { + fwc.use_destination = 1; + } /* Let us find it via our opts struct as well. */ @@ -1042,13 +1056,14 @@ process_spa_request(const fko_srv_options_t * const opts, in_chain->table, ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP) ple->port, exp_ts, in_chain->target ); if(rule_exists(opts, in_chain, rule_buf, - ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + ple->proto, spadat->use_src_ip, spadat->pkt_destination_ip, ple->port, exp_ts) == 0) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { @@ -1078,13 +1093,14 @@ process_spa_request(const fko_srv_options_t * const opts, out_chain->table, ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP), ple->port, exp_ts, out_chain->target ); if(rule_exists(opts, out_chain, rule_buf, - ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + ple->proto, spadat->use_src_ip, spadat->pkt_destination_ip, ple->port, exp_ts) == 0) { if(create_rule(opts, out_chain->to_chain, rule_buf)) { @@ -1151,6 +1167,7 @@ process_spa_request(const fko_srv_options_t * const opts, in_chain->table, fst_proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP), nat_port, exp_ts, in_chain->target @@ -1166,7 +1183,7 @@ process_spa_request(const fko_srv_options_t * const opts, add_jump_rule(opts, FIREWD_INPUT_ACCESS); if(rule_exists(opts, in_chain, rule_buf, - fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0) + fst_proto, spadat->use_src_ip, spadat->pkt_destination_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { @@ -1209,7 +1226,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, fwd_chain, rule_buf, fst_proto, - spadat->use_src_ip, nat_port, exp_ts) == 0) + spadat->use_src_ip, nat_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, fwd_chain->to_chain, rule_buf)) { @@ -1245,6 +1262,7 @@ process_spa_request(const fko_srv_options_t * const opts, dnat_chain->table, fst_proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : FIREWD_ANY_IP), fst_port, exp_ts, dnat_chain->target, @@ -1253,7 +1271,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, dnat_chain, rule_buf, fst_proto, - spadat->use_src_ip, fst_port, exp_ts) == 0) + spadat->use_src_ip, spadat->pkt_destination_ip, fst_port, exp_ts) == 0) { if(create_rule(opts, dnat_chain->to_chain, rule_buf)) { @@ -1333,7 +1351,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, snat_chain, rule_buf, fst_proto, - spadat->use_src_ip, nat_port, exp_ts) == 0) + spadat->use_src_ip, NULL, nat_port, exp_ts) == 0) { if(create_rule(opts, snat_chain->to_chain, rule_buf)) { diff --git a/server/fw_util_firewalld.h b/server/fw_util_firewalld.h index 8b17c4c9..b91131d2 100644 --- a/server/fw_util_firewalld.h +++ b/server/fw_util_firewalld.h @@ -42,10 +42,10 @@ /* firewalld command args */ #define FIREWD_CHK_RULE_ARGS "-C %s %s" /* the other macros add SH_REDIR if necessary */ -#define FIREWD_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR -#define FIREWD_OUT_RULE_ARGS "-t %s -p %i -d %s --sport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR +#define FIREWD_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR +#define FIREWD_OUT_RULE_ARGS "-t %s -p %i -d %s -s %s --sport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR #define FIREWD_FWD_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR -#define FIREWD_DNAT_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i" SH_REDIR +#define FIREWD_DNAT_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i" SH_REDIR #define FIREWD_SNAT_RULE_ARGS "-t %s -p %i -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s %s" SH_REDIR #define FIREWD_TMP_COMMENT_ARGS "-t %s -I %s %i -s 127.0.0.2 -m comment --comment " TMP_COMMENT " -j %s" SH_REDIR #define FIREWD_TMP_CHK_RULE_ARGS "-t %s -I %s %i -s 127.0.0.2 -p udp -j %s" SH_REDIR @@ -60,6 +60,7 @@ #define FIREWD_DEL_JUMP_RULE_ARGS "-t %s -D %s -j %s" SH_REDIR /* let firewalld work out the rule number */ #define FIREWD_LIST_RULES_ARGS "-t %s -L %s --line-numbers -n" SH_REDIR #define FIREWD_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers" SH_REDIR +#define FIREWD_ANY_IP "0.0.0.0/0" int validate_firewd_chain_conf(const char * const chain_str); diff --git a/server/fw_util_ipf.c b/server/fw_util_ipf.c index 1e875b91..dbb4f499 100644 --- a/server/fw_util_ipf.c +++ b/server/fw_util_ipf.c @@ -72,7 +72,11 @@ fw_config_init(fko_srv_options_t *opts) /* Set our firewall exe command path (iptables in most cases). */ strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command)); - + + if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0) + { + fwc.use_destination = 1; + } /* Let us find it via our opts struct as well. */ diff --git a/server/fw_util_ipf.h b/server/fw_util_ipf.h index 966607d5..cd023b28 100644 --- a/server/fw_util_ipf.h +++ b/server/fw_util_ipf.h @@ -40,6 +40,7 @@ #define IPF_ADD_SNAT_RULE_ARGS "" #define IPF_DEL_RULE_ARGS "" #define IPF_LIST_RULES_ARGS "" +#define IPF_ANY_IP "" #endif /* FW_UTIL_IPF_H */ diff --git a/server/fw_util_ipfw.c b/server/fw_util_ipfw.c index 932164f9..1025b4e6 100644 --- a/server/fw_util_ipfw.c +++ b/server/fw_util_ipfw.c @@ -238,6 +238,11 @@ fw_config_init(fko_srv_options_t * const opts) RCHK_MAX_IPFW_PURGE_INTERVAL); return 0; } + + if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0) + { + fwc.use_destination = 1; + } /* Let us find it via our opts struct as well. */ @@ -532,6 +537,7 @@ process_spa_request(const fko_srv_options_t * const opts, fwc.active_set_num, ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : IPFW_ANY_IP), ple->port, exp_ts ); diff --git a/server/fw_util_ipfw.h b/server/fw_util_ipfw.h index fa716761..031221fa 100644 --- a/server/fw_util_ipfw.h +++ b/server/fw_util_ipfw.h @@ -40,13 +40,14 @@ enum { /* ipfw command args */ -#define IPFW_ADD_RULE_ARGS "add %u set %u pass %u from %s to me dst-port %u setup keep-state // " EXPIRE_COMMENT_PREFIX "%u" +#define IPFW_ADD_RULE_ARGS "add %u set %u pass %u from %s to %s dst-port %u setup keep-state // " EXPIRE_COMMENT_PREFIX "%u" #define IPFW_ADD_CHECK_STATE_ARGS "add %u set %u check-state" #define IPFW_MOVE_RULE_ARGS "set move rule %u to %u" #define IPFW_MOVE_SET_ARGS "set move %u to %u" #define IPFW_DISABLE_SET_ARGS "set disable %u" #define IPFW_LIST_ALL_RULES_ARGS "list" #define IPFW_DEL_RULE_SET_ARGS "delete set %u" +#define IPFW_ANY_IP "me" #ifdef __APPLE__ #define IPFW_DEL_RULE_ARGS "delete %u" //--DSS diff args diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index 8492fa83..55fb6e84 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -70,14 +70,15 @@ chop_newline(char *str) static int rule_exists_no_chk_support(const fko_srv_options_t * const opts, const struct fw_chain * const fwc, const unsigned int proto, - const char * const ip, const unsigned int port, - const unsigned int exp_ts) + const char * const srcip, const char * const dstip, + const unsigned int port, const unsigned int exp_ts) { int rule_exists=0, rule_num=0, rtmp=0; char cmd_buf[CMD_BUFSIZE] = {0}; char target_search[CMD_BUFSIZE] = {0}; char proto_search[CMD_BUFSIZE] = {0}; - char ip_search[CMD_BUFSIZE] = {0}; + char srcip_search[CMD_BUFSIZE] = {0}; + char dstip_search[CMD_BUFSIZE] = {0}; char port_search[CMD_BUFSIZE] = {0}; char exp_ts_search[CMD_BUFSIZE] = {0}; @@ -109,7 +110,11 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, snprintf(port_search, CMD_BUFSIZE-1, ":%u ", port); snprintf(target_search, CMD_BUFSIZE-1, " %s ", fwc->target); - snprintf(ip_search, CMD_BUFSIZE-1, " %s ", ip); + snprintf(srcip_search, CMD_BUFSIZE-1, " %s ", srcip); + if (dstip != NULL) + { + snprintf(dstip_search, CMD_BUFSIZE-1, " %s ", dstip); + } snprintf(exp_ts_search, CMD_BUFSIZE-1, "%u ", exp_ts); /* search for each of the substrings, and require the returned @@ -125,25 +130,28 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts, NO_TIMEOUT, proto_search, &pid_status, opts); if(rtmp == rule_num) rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, ip_search, &pid_status, opts); + NO_TIMEOUT, srcip_search, &pid_status, opts); if(rtmp == rule_num) - rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, target_search, &pid_status, opts); + rtmp = (dstip == NULL) ? rtmp : search_extcmd(cmd_buf, WANT_STDERR, + NO_TIMEOUT, dstip_search, &pid_status, opts); if(rtmp == rule_num) rtmp = search_extcmd(cmd_buf, WANT_STDERR, - NO_TIMEOUT, port_search, &pid_status, opts); + NO_TIMEOUT, target_search, &pid_status, opts); if(rtmp == rule_num) - rule_exists = 1; + rtmp = search_extcmd(cmd_buf, WANT_STDERR, + NO_TIMEOUT, port_search, &pid_status, opts); + if(rtmp == rule_num) + rule_exists = 1; } if(rule_exists) log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule (already exists", - proto_search, port, ip, exp_ts); + proto_search, port, srcip, exp_ts); else log_msg(LOG_DEBUG, "rule_exists_no_chk_support() %s %u -> %s expires: %u rule does not exist", - proto_search, port, ip, exp_ts); + proto_search, port, srcip, exp_ts); return(rule_exists); } @@ -185,15 +193,16 @@ rule_exists_chk_support(const fko_srv_options_t * const opts, static int rule_exists(const fko_srv_options_t * const opts, const struct fw_chain * const fwc, const char * const rule, - const unsigned int proto, const char * const ip, - const unsigned int port, const unsigned int exp_ts) + const unsigned int proto, const char * const srcip, + const char * const dstip, const unsigned int port, + const unsigned int exp_ts) { int rule_exists = 0; if(have_ipt_chk_support == 1) rule_exists = rule_exists_chk_support(opts, fwc->to_chain, rule); else - rule_exists = rule_exists_no_chk_support(opts, fwc, proto, ip, port, exp_ts); + rule_exists = rule_exists_no_chk_support(opts, fwc, proto, srcip, (opts->fw_config->use_destination ? dstip : NULL), port, exp_ts); if(rule_exists == 1) log_msg(LOG_DEBUG, "rule_exists() Rule : '%s' in %s already exists", @@ -859,6 +868,11 @@ fw_config_init(fko_srv_options_t * const opts) } } } + + if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0) + { + fwc.use_destination = 1; + } /* Let us find it via our opts struct as well. */ @@ -1044,13 +1058,14 @@ process_spa_request(const fko_srv_options_t * const opts, in_chain->table, ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), ple->port, exp_ts, in_chain->target ); if(rule_exists(opts, in_chain, rule_buf, - ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + ple->proto, spadat->use_src_ip, spadat->pkt_destination_ip, ple->port, exp_ts) == 0) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { @@ -1080,13 +1095,14 @@ process_spa_request(const fko_srv_options_t * const opts, out_chain->table, ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), ple->port, exp_ts, out_chain->target ); if(rule_exists(opts, out_chain, rule_buf, - ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0) + ple->proto, spadat->use_src_ip, spadat->pkt_destination_ip, ple->port, exp_ts) == 0) { if(create_rule(opts, out_chain->to_chain, rule_buf)) { @@ -1153,6 +1169,7 @@ process_spa_request(const fko_srv_options_t * const opts, in_chain->table, fst_proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), nat_port, exp_ts, in_chain->target @@ -1168,7 +1185,7 @@ process_spa_request(const fko_srv_options_t * const opts, add_jump_rule(opts, IPT_INPUT_ACCESS); if(rule_exists(opts, in_chain, rule_buf, - fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0) + fst_proto, spadat->use_src_ip, spadat->pkt_destination_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { @@ -1211,7 +1228,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, fwd_chain, rule_buf, fst_proto, - spadat->use_src_ip, nat_port, exp_ts) == 0) + spadat->use_src_ip, nat_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, fwd_chain->to_chain, rule_buf)) { @@ -1247,6 +1264,7 @@ process_spa_request(const fko_srv_options_t * const opts, dnat_chain->table, fst_proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), fst_port, exp_ts, dnat_chain->target, @@ -1255,7 +1273,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, dnat_chain, rule_buf, fst_proto, - spadat->use_src_ip, fst_port, exp_ts) == 0) + spadat->use_src_ip, spadat->pkt_destination_ip, fst_port, exp_ts) == 0) { if(create_rule(opts, dnat_chain->to_chain, rule_buf)) { @@ -1335,7 +1353,7 @@ process_spa_request(const fko_srv_options_t * const opts, ); if(rule_exists(opts, snat_chain, rule_buf, fst_proto, - spadat->use_src_ip, nat_port, exp_ts) == 0) + spadat->use_src_ip, NULL, nat_port, exp_ts) == 0) { if(create_rule(opts, snat_chain->to_chain, rule_buf)) { diff --git a/server/fw_util_iptables.h b/server/fw_util_iptables.h index 626e0ec5..27794efc 100644 --- a/server/fw_util_iptables.h +++ b/server/fw_util_iptables.h @@ -42,10 +42,10 @@ /* iptables command args */ #define IPT_CHK_RULE_ARGS "-C %s %s" /* the other macros add SH_REDIR if necessary */ -#define IPT_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR -#define IPT_OUT_RULE_ARGS "-t %s -p %i -d %s --sport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR +#define IPT_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR +#define IPT_OUT_RULE_ARGS "-t %s -p %i -d %s -s %s --sport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR #define IPT_FWD_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" SH_REDIR -#define IPT_DNAT_RULE_ARGS "-t %s -p %i -s %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i" SH_REDIR +#define IPT_DNAT_RULE_ARGS "-t %s -p %i -s %s -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s --to-destination %s:%i" SH_REDIR #define IPT_SNAT_RULE_ARGS "-t %s -p %i -d %s --dport %i -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s %s" SH_REDIR #define IPT_TMP_COMMENT_ARGS "-t %s -I %s %i -s 127.0.0.2 -m comment --comment " TMP_COMMENT " -j %s" SH_REDIR #define IPT_TMP_CHK_RULE_ARGS "-t %s -I %s %i -s 127.0.0.2 -p udp -j %s" SH_REDIR @@ -60,6 +60,7 @@ #define IPT_DEL_JUMP_RULE_ARGS "-t %s -D %s -j %s" SH_REDIR /* let iptables work out the rule number */ #define IPT_LIST_RULES_ARGS "-t %s -L %s --line-numbers -n" SH_REDIR #define IPT_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers" SH_REDIR +#define IPT_ANY_IP "0.0.0.0/0" int validate_ipt_chain_conf(const char * const chain_str); diff --git a/server/fw_util_pf.c b/server/fw_util_pf.c index 9d76033c..f41bf7a7 100644 --- a/server/fw_util_pf.c +++ b/server/fw_util_pf.c @@ -151,6 +151,11 @@ fw_config_init(fko_srv_options_t * const opts) /* Set the PF anchor name */ strlcpy(fwc.anchor, opts->config[CONF_PF_ANCHOR_NAME], sizeof(fwc.anchor)); + + if(strncasecmp(opts->config[CONF_ENABLE_DESTINATION_RULE], "Y", 1)==0) + { + fwc.use_destination = 1; + } /* Let us find it via our opts struct as well. */ @@ -243,6 +248,7 @@ process_spa_request(const fko_srv_options_t * const opts, snprintf(new_rule, MAX_PF_NEW_RULE_LEN-1, PF_ADD_RULE_ARGS "\n", ple->proto, spadat->use_src_ip, + (fwc.use_destination ? spadat->pkt_destination_ip : PF_ANY_IP), ple->port, exp_ts ); diff --git a/server/fw_util_pf.h b/server/fw_util_pf.h index e1c6f093..9d4901eb 100644 --- a/server/fw_util_pf.h +++ b/server/fw_util_pf.h @@ -32,7 +32,7 @@ #define FW_UTIL_PF_H #define MAX_PF_ANCHOR_SEARCH_LEN (MAX_PF_ANCHOR_LEN+11) /* room for 'anchor "' string */ -#define MAX_PF_NEW_RULE_LEN 120 +#define MAX_PF_NEW_RULE_LEN 140 #if HAVE_EXECVPE #define SH_REDIR "" /* the shell is not used when execvpe() is available */ @@ -42,7 +42,7 @@ /* pf command args */ -#define PF_ADD_RULE_ARGS "pass in quick proto %u from %s to any port %u keep state label " EXPIRE_COMMENT_PREFIX "%u" +#define PF_ADD_RULE_ARGS "pass in quick proto %u from %s to %s port %u keep state label " EXPIRE_COMMENT_PREFIX "%u" #define PF_WRITE_ANCHOR_RULES_ARGS "-a %s -f -" #if HAVE_EXECVPE #define PF_LIST_ANCHOR_RULES_ARGS "-a %s -s rules" @@ -51,6 +51,7 @@ #endif #define PF_ANCHOR_CHECK_ARGS "-s Anchor" SH_REDIR /* to check for fwknop anchor */ #define PF_DEL_ALL_ANCHOR_RULES "-a %s -F all" SH_REDIR +#define PF_ANY_IP "any" #endif /* FW_UTIL_PF_H */ diff --git a/server/fwknopd.8.in b/server/fwknopd.8.in index 7663ecbe..6b167aa7 100644 --- a/server/fwknopd.8.in +++ b/server/fwknopd.8.in @@ -526,6 +526,13 @@ Specify the directory where writes run time state files\&. The default is \fI@localstatedir@/run\fR\&. .RE +.PP +\fBENABLE_DESTINATION_RULE\fR \fI\fR +.RS 4 +Controls whether +\fBfwknopd\fR +will set the destination field on the firewall rule to the destination address specified on the incoming SPA packet\&. This is useful for interfaces with multiple IP addresses hosting separate services\&. If \(lqENABLE_IPT_OUTPUT\(rq is set to \(lqY\(rq, the source field of the firewall rule is set\&. FORWARD and SNAT rules are not affected however, DNAT rules will also have their destination field set\&. The default is \(lqN\(rq, which sets the destination field to 0\&.0\&.0\&.0/0 (any)\&. +.RE .SS "ACCESS\&.CONF VARIABLES" .sp This section describes the access control directives in the \fI@sysconfdir@/fwknop/access\&.conf\fR file\&. Theses directives define encryption keys and level of access that is granted to \fBfwknop\fR clients that have generated the appropriate encrypted message\&. @@ -539,6 +546,11 @@ This defines the source address from which the SPA packet will be accepted\&. Th definition must start with the \(lqSOURCE\(rq keyword\&. Networks should be specified in CIDR notation (e\&.g\&. \(lq192\&.168\&.10\&.0/24\(rq), and individual IP addresses can be specified as well\&. Also, multiple IP\(cqs and/or networks can be defined as a comma separated list (e\&.g\&. \(lq192\&.168\&.10\&.0/24,10\&.1\&.1\&.123\(rq) .RE .PP +\fBDESTINATION\fR \fI\fR +.RS 4 +This defines the destination address to which the SPA packet will be accepted\&. The string \(lqANY\(rq is also accepted if a valid SPA packet should be honored to any destination IP\&. Networks should be specified in CIDR notation (e\&.g\&. \(lq192\&.168\&.10\&.0/24\(rq), and individual IP addresses can be specified as well\&. Also, multiple IP\(cqs and/or networks can be defined as a comma separated list (e\&.g\&. \(lq192\&.168\&.10\&.0/24,10\&.1\&.1\&.123\(rq) +.RE +.PP \fBOPEN_PORTS\fR \fI,\&...,\fR .RS 4 Define a set of ports and protocols (tcp or udp) that will be opened if a valid knock sequence is seen\&. If this entry is not set, diff --git a/server/fwknopd.conf b/server/fwknopd.conf index 8faeebd6..29ec9e2d 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -152,6 +152,16 @@ # # ENABLE_PCAP_ANY_DIRECTION N; +# Controls whether fwknopd will set the destination field on the firewall +# rule to the destination address specified on the incoming SPA packet. +# This is useful for interfaces with multiple IP addresses hosting separate +# services. If ENABLE_IPT_OUTPUT is set to "Y", the source field of +# the firewall rule is set. FORWARD and SNAT rules are not affected however, +# DNAT rules will also have their destination field set. The default is +# "N", which sets the destination field to 0.0.0.0/0 (any). +# +# ENABLE_DESTINATION_RULE Y; + ############################################################################## # 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 f06b6e15..350aeb3d 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -110,6 +110,7 @@ #define DEF_UDPSERV_SELECT_TIMEOUT "500000" /* half a second (in microseconds) */ #define DEF_SYSLOG_IDENTITY MY_NAME #define DEF_SYSLOG_FACILITY "LOG_DAEMON" +#define DEF_ENABLE_DESTINATION_RULE "N" #define DEF_FW_ACCESS_TIMEOUT 30 @@ -247,6 +248,7 @@ enum { //CONF_EXTERNAL_CMD_ALARM, //CONF_ENABLE_EXT_CMD_PREFIX, //CONF_EXT_CMD_PREFIX, + CONF_ENABLE_DESTINATION_RULE, #if FIREWALL_FIREWALLD CONF_ENABLE_FIREWD_FORWARDING, CONF_ENABLE_FIREWD_LOCAL_NAT, @@ -345,6 +347,8 @@ typedef struct acc_stanza { char *source; acc_int_list_t *source_list; + char *destination; + acc_int_list_t *destination_list; char *open_ports; acc_port_list_t *oport_list; char *restrict_ports; @@ -446,6 +450,9 @@ typedef struct acc_stanza /* Flag for firewalld SNAT vs. MASQUERADE usage */ unsigned char use_masquerade; + /* Flag for setting destination field in rule + */ + unsigned char use_destination; }; #elif FIREWALL_IPTABLES @@ -493,6 +500,9 @@ typedef struct acc_stanza /* Flag for iptables SNAT vs. MASQUERADE usage */ unsigned char use_masquerade; + /* Flag for setting destination field in rule + */ + unsigned char use_destination; }; #elif FIREWALL_IPFW @@ -509,6 +519,7 @@ typedef struct acc_stanza time_t next_expire; time_t last_purge; char fw_command[MAX_PATH_LEN]; + unsigned char use_destination; }; #elif FIREWALL_PF @@ -520,6 +531,7 @@ typedef struct acc_stanza time_t next_expire; char anchor[MAX_PF_ANCHOR_LEN]; char fw_command[MAX_PATH_LEN]; + unsigned char use_destination; }; #elif FIREWALL_IPF @@ -552,6 +564,7 @@ typedef struct spa_data char *spa_message; char spa_message_src_ip[MAX_IPV4_STR_LEN]; char pkt_source_ip[MAX_IPV4_STR_LEN]; + char pkt_destination_ip[MAX_IPV4_STR_LEN]; char spa_message_remain[1024]; /* --DSS FIXME: arbitrary bounds */ char *nat_access; char *server_auth; diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 544f102e..a49291c0 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -320,6 +320,9 @@ incoming_spa(fko_srv_options_t *opts) inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + + inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), + spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); /* At this point, we want to validate and (if needed) preprocess the * SPA data and/or to be reasonably sure we have a SPA packet (i.e @@ -405,9 +408,10 @@ incoming_spa(fko_srv_options_t *opts) ctx = NULL; } - /* Check for a match for the SPA source IP and the access stanza + /* Check for a match for the SPA source and destination IP and the access stanza */ - if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip))) + if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)) || + (acc->destination_list != NULL && ! compare_addr_list(acc->destination_list, ntohl(spa_pkt->packet_dst_ip)))) { acc = acc->next; continue; diff --git a/test/conf/destination_rule_fwknopd.conf b/test/conf/destination_rule_fwknopd.conf new file mode 100644 index 00000000..670a3ca5 --- /dev/null +++ b/test/conf/destination_rule_fwknopd.conf @@ -0,0 +1 @@ +ENABLE_DESTINATION_RULE Y; diff --git a/test/tests/basic_operations.pl b/test/tests/basic_operations.pl index 91f3ecb1..93eb3351 100644 --- a/test/tests/basic_operations.pl +++ b/test/tests/basic_operations.pl @@ -3335,6 +3335,22 @@ 'positive_output_matches' => [qr/No\skeys\sfound/], }, + { + 'category' => 'basic operations', + 'subcategory' => 'server', + 'detail' => 'access DESTINATION key', + 'function' => \&server_conf_files, + 'fwknopd_cmdline' => $server_rewrite_conf_files, + 'exec_err' => $YES, + 'server_access_file' => [ + 'DESTINATION 1.1.1.1', + ], + 'server_conf_file' => [ + '### comment line' + ], + 'positive_output_matches' => [qr/No\skeys\sfound/], + }, + { 'category' => 'basic operations', 'subcategory' => 'server', From d0849d6cc9d641d580a9f39811d160db5e910f98 Mon Sep 17 00:00:00 2001 From: Grant Pannell Date: Sat, 29 Nov 2014 15:10:38 +1030 Subject: [PATCH 2/9] Fix type on man asciidoc --- doc/fwknopd.man.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/fwknopd.man.asciidoc b/doc/fwknopd.man.asciidoc index e0af2a2d..7707be27 100644 --- a/doc/fwknopd.man.asciidoc +++ b/doc/fwknopd.man.asciidoc @@ -422,7 +422,7 @@ directive starts a new stanza. ``192.168.10.0/24,10.1.1.123'') *DESTINATION* '':: - This defines the destination address which the SPA packet will be + This defines the destination address for which the SPA packet will be accepted. The string ``ANY'' is also accepted if a valid SPA packet should be honored to any destination IP. Networks should be specified in CIDR notation (e.g. ``192.168.10.0/24''), From af6087c48d8457f1fbb8cd82bc8dec482155d45f Mon Sep 17 00:00:00 2001 From: Grant Pannell Date: Sat, 29 Nov 2014 15:14:31 +1030 Subject: [PATCH 3/9] Keep the documentation consistent --- server/access.conf | 2 +- server/fwknopd.8.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/access.conf b/server/access.conf index 22e90c96..17f7627e 100644 --- a/server/access.conf +++ b/server/access.conf @@ -38,7 +38,7 @@ # DESTINATION # -# This defines the destination address from which a SPA packet will be accepted. +# This defines the destination address for which a SPA packet will be accepted. # Networks should be specified in CIDR (e.g. "192.168.10.0/24") notation. # Individual IP addresses can be specified as well. # diff --git a/server/fwknopd.8.in b/server/fwknopd.8.in index 6b167aa7..5c28cb3d 100644 --- a/server/fwknopd.8.in +++ b/server/fwknopd.8.in @@ -548,7 +548,7 @@ definition must start with the \(lqSOURCE\(rq keyword\&. Networks should be spec .PP \fBDESTINATION\fR \fI\fR .RS 4 -This defines the destination address to which the SPA packet will be accepted\&. The string \(lqANY\(rq is also accepted if a valid SPA packet should be honored to any destination IP\&. Networks should be specified in CIDR notation (e\&.g\&. \(lq192\&.168\&.10\&.0/24\(rq), and individual IP addresses can be specified as well\&. Also, multiple IP\(cqs and/or networks can be defined as a comma separated list (e\&.g\&. \(lq192\&.168\&.10\&.0/24,10\&.1\&.1\&.123\(rq) +This defines the destination address for which the SPA packet will be accepted\&. The string \(lqANY\(rq is also accepted if a valid SPA packet should be honored to any destination IP\&. Networks should be specified in CIDR notation (e\&.g\&. \(lq192\&.168\&.10\&.0/24\(rq), and individual IP addresses can be specified as well\&. Also, multiple IP\(cqs and/or networks can be defined as a comma separated list (e\&.g\&. \(lq192\&.168\&.10\&.0/24,10\&.1\&.1\&.123\(rq) .RE .PP \fBOPEN_PORTS\fR \fI,\&...,\fR From 42d103d100af7839a74816c9bd1d6aafdff6ea78 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sun, 30 Nov 2014 17:02:27 -0500 Subject: [PATCH 4/9] [test suite] minor test update for DESTINATION access.conf stanza --- test/tests/basic_operations.pl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/tests/basic_operations.pl b/test/tests/basic_operations.pl index 93eb3351..b334ef3a 100644 --- a/test/tests/basic_operations.pl +++ b/test/tests/basic_operations.pl @@ -3338,7 +3338,7 @@ { 'category' => 'basic operations', 'subcategory' => 'server', - 'detail' => 'access DESTINATION key', + 'detail' => 'access DESTINATION missing SOURCE', 'function' => \&server_conf_files, 'fwknopd_cmdline' => $server_rewrite_conf_files, 'exec_err' => $YES, @@ -3348,6 +3348,22 @@ 'server_conf_file' => [ '### comment line' ], + 'positive_output_matches' => [qr/not\sfind\svalid\sSOURCE/], + }, + { + 'category' => 'basic operations', + 'subcategory' => 'server', + 'detail' => 'missing access DESTINATION key', + 'function' => \&server_conf_files, + 'fwknopd_cmdline' => $server_rewrite_conf_files, + 'exec_err' => $YES, + 'server_access_file' => [ + 'SOURCE 1.1.1.1', + 'DESTINATION 1.2.3.4', + ], + 'server_conf_file' => [ + '### comment line' + ], 'positive_output_matches' => [qr/No\skeys\sfound/], }, From e1694c732c302522741e9c515f1eb3e39cfefc54 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sun, 30 Nov 2014 21:36:46 -0500 Subject: [PATCH 5/9] update ChangeLog to include Grant's patch --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 4e17e5ce..37b13cb2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,12 @@ fwknop-2.6.5 (11//2014): + - [server] (Grant Pannell) Added a new access.conf variable "DESTINATION" + to define the destination address for which an SPA packet will be + accepted. The string "ANY" is also accepted if a valid SPA packet should + be honored to any destination IP. Similarly to the "SOURCE" variable, + networks should be specified in CIDR notation (e.g. "192.168.10.0/24"), + and individual IP addresses can be specified as well. Also, multiple IP's + and/or networks can be defined as a comma separated list (e.g. + "192.168.10.0/24,10.1.1.123). - [server] Bug fix to ensure that proper bounds are enforced when importing digest cache files from previous fwknopd executions. This bug was discovered through fuzzing with American Fuzzy Lop (AFL) as driven From cf84f016a38c0d630f7d3d832075111a10cbc420 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sun, 30 Nov 2014 21:37:42 -0500 Subject: [PATCH 6/9] [test suite] add complete cycle DESTINATION test --- Makefile.am | 1 + test/conf/hmac_spa_destination_access.conf | 5 +++++ test/test-fwknop.pl | 2 ++ test/tests/rijndael_hmac.pl | 13 +++++++++++++ 4 files changed, 21 insertions(+) create mode 100644 test/conf/hmac_spa_destination_access.conf diff --git a/Makefile.am b/Makefile.am index d9cda93e..21ea41f2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -315,6 +315,7 @@ EXTRA_DIST = \ test/conf/ipt_snat_no_translate_ip_fwknopd.conf \ test/conf/firewd_snat_no_translate_ip_fwknopd.conf \ test/conf/destination_rule_fwknopd.conf \ + test/conf/hmac_spa_destination_access.conf \ test/conf/spa_replay.pcap \ test/conf/fcs_spa.pcap \ test/fko-wrapper/Makefile \ diff --git a/test/conf/hmac_spa_destination_access.conf b/test/conf/hmac_spa_destination_access.conf new file mode 100644 index 00000000..23d656de --- /dev/null +++ b/test/conf/hmac_spa_destination_access.conf @@ -0,0 +1,5 @@ +SOURCE ANY +DESTINATION ANY +KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg= +HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg== +FW_ACCESS_TIMEOUT 3 diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 686c2f54..4ae5c576 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -388,6 +388,8 @@ our %cf = ( 'hmac_simple_keys_access' => "$conf_dir/hmac_simple_keys_access.conf", 'hmac_invalid_type_access' => "$conf_dir/hmac_invalid_type_access.conf", 'hmac_cygwin_access' => "$conf_dir/hmac_no_b64_cygwin_access.conf", + 'spa_destnation' => "$conf_dir/destination_rule_fwknopd.conf", + 'hmac_spa_destination_access' => "$conf_dir/hmac_spa_destination_access.conf", 'exp_access' => "$conf_dir/expired_stanza_access.conf", 'future_exp_access' => "$conf_dir/future_expired_stanza_access.conf", 'exp_epoch_access' => "$conf_dir/expired_epoch_stanza_access.conf", diff --git a/test/tests/rijndael_hmac.pl b/test/tests/rijndael_hmac.pl index 3f466abc..e69f8491 100644 --- a/test/tests/rijndael_hmac.pl +++ b/test/tests/rijndael_hmac.pl @@ -83,6 +83,19 @@ 'key_file' => $cf{'rc_hmac_b64_key'}, 'client_cycles_per_server_instance' => 3, }, + { + 'category' => 'Rijndael+HMAC', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle DESTINATION', + 'function' => \&spa_cycle, + 'cmdline' => $default_client_hmac_args, + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} -a $cf{'hmac_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'key_file' => $cf{'rc_hmac_b64_key'}, + 'server_positive_output_matches' => [qr/\b$fake_ip\s.*$loopback_ip\b/], + }, { 'category' => 'Rijndael+HMAC', From 7a9a430f24e9690284c56691b5b03e5f2b6eec18 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sun, 30 Nov 2014 22:17:51 -0500 Subject: [PATCH 7/9] [test suite] additional DESTINATION accept/filter tests --- Makefile.am | 4 ++ test/conf/hmac_spa_destination2_access.conf | 5 ++ test/conf/hmac_spa_destination3_access.conf | 5 ++ test/conf/hmac_spa_destination4_access.conf | 5 ++ test/conf/hmac_spa_destination5_access.conf | 5 ++ test/test-fwknop.pl | 4 ++ test/tests/rijndael_hmac.pl | 56 +++++++++++++++++++-- 7 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 test/conf/hmac_spa_destination2_access.conf create mode 100644 test/conf/hmac_spa_destination3_access.conf create mode 100644 test/conf/hmac_spa_destination4_access.conf create mode 100644 test/conf/hmac_spa_destination5_access.conf diff --git a/Makefile.am b/Makefile.am index 21ea41f2..a1b4ae37 100644 --- a/Makefile.am +++ b/Makefile.am @@ -316,6 +316,10 @@ EXTRA_DIST = \ test/conf/firewd_snat_no_translate_ip_fwknopd.conf \ test/conf/destination_rule_fwknopd.conf \ test/conf/hmac_spa_destination_access.conf \ + test/conf/hmac_spa_destination2_access.conf \ + test/conf/hmac_spa_destination3_access.conf \ + test/conf/hmac_spa_destination4_access.conf \ + test/conf/hmac_spa_destination5_access.conf \ test/conf/spa_replay.pcap \ test/conf/fcs_spa.pcap \ test/fko-wrapper/Makefile \ diff --git a/test/conf/hmac_spa_destination2_access.conf b/test/conf/hmac_spa_destination2_access.conf new file mode 100644 index 00000000..5ab0f281 --- /dev/null +++ b/test/conf/hmac_spa_destination2_access.conf @@ -0,0 +1,5 @@ +SOURCE ANY +DESTINATION 1.2.3.4, 127.0.0.1, 123.0.0.0/24 +KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg= +HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg== +FW_ACCESS_TIMEOUT 3 diff --git a/test/conf/hmac_spa_destination3_access.conf b/test/conf/hmac_spa_destination3_access.conf new file mode 100644 index 00000000..f8482b4a --- /dev/null +++ b/test/conf/hmac_spa_destination3_access.conf @@ -0,0 +1,5 @@ +SOURCE ANY +DESTINATION 1.2.3.4, 123.0.0.0/24, 127.0.0.1/32 +KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg= +HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg== +FW_ACCESS_TIMEOUT 3 diff --git a/test/conf/hmac_spa_destination4_access.conf b/test/conf/hmac_spa_destination4_access.conf new file mode 100644 index 00000000..71e79727 --- /dev/null +++ b/test/conf/hmac_spa_destination4_access.conf @@ -0,0 +1,5 @@ +SOURCE ANY +DESTINATION 1.2.3.4 +KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg= +HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg== +FW_ACCESS_TIMEOUT 3 diff --git a/test/conf/hmac_spa_destination5_access.conf b/test/conf/hmac_spa_destination5_access.conf new file mode 100644 index 00000000..9ceacfeb --- /dev/null +++ b/test/conf/hmac_spa_destination5_access.conf @@ -0,0 +1,5 @@ +SOURCE ANY +DESTINATION 1.2.3.4, 123.0.0.0/24 +KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg= +HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg== +FW_ACCESS_TIMEOUT 3 diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 4ae5c576..93cfe139 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -390,6 +390,10 @@ our %cf = ( 'hmac_cygwin_access' => "$conf_dir/hmac_no_b64_cygwin_access.conf", 'spa_destnation' => "$conf_dir/destination_rule_fwknopd.conf", 'hmac_spa_destination_access' => "$conf_dir/hmac_spa_destination_access.conf", + 'hmac_spa_destination2_access' => "$conf_dir/hmac_spa_destination2_access.conf", + 'hmac_spa_destination3_access' => "$conf_dir/hmac_spa_destination3_access.conf", + 'hmac_spa_destination4_access' => "$conf_dir/hmac_spa_destination4_access.conf", + 'hmac_spa_destination5_access' => "$conf_dir/hmac_spa_destination5_access.conf", 'exp_access' => "$conf_dir/expired_stanza_access.conf", 'future_exp_access' => "$conf_dir/future_expired_stanza_access.conf", 'exp_epoch_access' => "$conf_dir/expired_epoch_stanza_access.conf", diff --git a/test/tests/rijndael_hmac.pl b/test/tests/rijndael_hmac.pl index e69f8491..3396072f 100644 --- a/test/tests/rijndael_hmac.pl +++ b/test/tests/rijndael_hmac.pl @@ -86,17 +86,67 @@ { 'category' => 'Rijndael+HMAC', 'subcategory' => 'client+server', - 'detail' => 'complete cycle DESTINATION', + 'detail' => 'cycle DESTINATION accepted (1)', 'function' => \&spa_cycle, 'cmdline' => $default_client_hmac_args, - 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} -a $cf{'hmac_access'} " . + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} " . + "-a $cf{'hmac_spa_destination_access'} " . "-d $default_digest_file -p $default_pid_file $intf_str", 'fw_rule_created' => $NEW_RULE_REQUIRED, 'fw_rule_removed' => $NEW_RULE_REMOVED, 'key_file' => $cf{'rc_hmac_b64_key'}, 'server_positive_output_matches' => [qr/\b$fake_ip\s.*$loopback_ip\b/], }, - + { + 'category' => 'Rijndael+HMAC', + 'subcategory' => 'client+server', + 'detail' => 'cycle DESTINATION accepted (2)', + 'function' => \&spa_cycle, + 'cmdline' => $default_client_hmac_args, + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} " . + "-a $cf{'hmac_spa_destination2_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'key_file' => $cf{'rc_hmac_b64_key'}, + }, + { + 'category' => 'Rijndael+HMAC', + 'subcategory' => 'client+server', + 'detail' => 'cycle DESTINATION accepted (3)', + 'function' => \&spa_cycle, + 'cmdline' => $default_client_hmac_args, + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} " . + "-a $cf{'hmac_spa_destination3_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'key_file' => $cf{'rc_hmac_b64_key'}, + }, + { + 'category' => 'Rijndael+HMAC', + 'subcategory' => 'client+server', + 'detail' => 'cycle DESTINATION filtered (1)', + 'function' => \&spa_cycle, + 'cmdline' => $default_client_hmac_args, + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} " . + "-a $cf{'hmac_spa_destination4_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $REQUIRE_NO_NEW_RULE, + 'key_file' => $cf{'rc_hmac_b64_key'}, + }, + { + 'category' => 'Rijndael+HMAC', + 'subcategory' => 'client+server', + 'detail' => 'cycle DESTINATION filtered (2)', + 'function' => \&spa_cycle, + 'cmdline' => $default_client_hmac_args, + 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'spa_destnation'} " . + "-a $cf{'hmac_spa_destination5_access'} " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $REQUIRE_NO_NEW_RULE, + 'key_file' => $cf{'rc_hmac_b64_key'}, + }, { 'category' => 'Rijndael+HMAC', 'subcategory' => 'client+server', From c5c263c02ff0bd1b68509b647176e381d3453ed2 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Wed, 3 Dec 2014 20:56:47 -0500 Subject: [PATCH 8/9] add Grant Pannell --- CREDITS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CREDITS b/CREDITS index c5094215..d8690be7 100644 --- a/CREDITS +++ b/CREDITS @@ -199,3 +199,13 @@ Bill Stubbs following platform: BeagleBone Black rev C running 3.8.13-bone50 #1 SMP Tue May 13 13:24:52 UTC 2014 armv7l GNU/Linux + +Grant Pannell + - Submitted a patch to add a new access.conf variable "DESTINATION" in + order to define the destination address for which an SPA packet will be + accepted. The string "ANY" is also accepted if a valid SPA packet should + be honored to any destination IP. Similarly to the "SOURCE" variable, + networks should be specified in CIDR notation (e.g. "192.168.10.0/24"), + and individual IP addresses can be specified as well. Also, multiple IP's + and/or networks can be defined as a comma separated list (e.g. + "192.168.10.0/24,10.1.1.123"). From d6dee352af535d8a8486fbf6e8facf4651d5cb13 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Wed, 3 Dec 2014 20:57:06 -0500 Subject: [PATCH 9/9] minor update to get DESTINATION filtering tests passing --- ChangeLog | 2 +- server/access.c | 8 ++++---- server/incoming_spa.c | 5 ++++- test/tests/rijndael_hmac.pl | 4 ++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 37b13cb2..3a263de7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,7 +6,7 @@ fwknop-2.6.5 (11//2014): networks should be specified in CIDR notation (e.g. "192.168.10.0/24"), and individual IP addresses can be specified as well. Also, multiple IP's and/or networks can be defined as a comma separated list (e.g. - "192.168.10.0/24,10.1.1.123). + "192.168.10.0/24,10.1.1.123"). - [server] Bug fix to ensure that proper bounds are enforced when importing digest cache files from previous fwknopd executions. This bug was discovered through fuzzing with American Fuzzy Lop (AFL) as driven diff --git a/server/access.c b/server/access.c index a7ae679a..c5901da9 100644 --- a/server/access.c +++ b/server/access.c @@ -1752,19 +1752,19 @@ parse_access_file(fko_srv_options_t *opts) } int -compare_addr_list(acc_int_list_t *source_list, const uint32_t ip) +compare_addr_list(acc_int_list_t *ip_list, const uint32_t ip) { int match = 0; - while(source_list) + while(ip_list) { - if((ip & source_list->mask) == (source_list->maddr & source_list->mask)) + if((ip & ip_list->mask) == (ip_list->maddr & ip_list->mask)) { match = 1; break; } - source_list = source_list->next; + ip_list = ip_list->next; } return(match); diff --git a/server/incoming_spa.c b/server/incoming_spa.c index a49291c0..9163b0a2 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -320,7 +320,7 @@ incoming_spa(fko_srv_options_t *opts) inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); - + inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); @@ -413,6 +413,9 @@ incoming_spa(fko_srv_options_t *opts) if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)) || (acc->destination_list != NULL && ! compare_addr_list(acc->destination_list, ntohl(spa_pkt->packet_dst_ip)))) { + log_msg(LOG_DEBUG, + "(stanza #%d) SPA packet (%s -> %s) filtered by SOURCE and/or DESTINATION criteria", + stanza_num, spadat.pkt_source_ip, spadat.pkt_destination_ip); acc = acc->next; continue; } diff --git a/test/tests/rijndael_hmac.pl b/test/tests/rijndael_hmac.pl index 3396072f..5ec9d0b9 100644 --- a/test/tests/rijndael_hmac.pl +++ b/test/tests/rijndael_hmac.pl @@ -133,6 +133,8 @@ "-a $cf{'hmac_spa_destination4_access'} " . "-d $default_digest_file -p $default_pid_file $intf_str", 'fw_rule_created' => $REQUIRE_NO_NEW_RULE, + 'client_pkt_tries' => 2, + 'server_receive_re' => qr/SPA\spacket\s.*filtered\sby\sSOURCE.*DEST/, 'key_file' => $cf{'rc_hmac_b64_key'}, }, { @@ -145,6 +147,8 @@ "-a $cf{'hmac_spa_destination5_access'} " . "-d $default_digest_file -p $default_pid_file $intf_str", 'fw_rule_created' => $REQUIRE_NO_NEW_RULE, + 'client_pkt_tries' => 2, + 'server_receive_re' => qr/SPA\spacket\s.*filtered\sby\sSOURCE.*DEST/, 'key_file' => $cf{'rc_hmac_b64_key'}, }, {