[server] Add FORWARD_ALL access.conf wildcard

This is a significant commit that allows iptables firewalls to be used
as an "SPA gateway" for all ports/protocols upon providing a valid SPA
packet. Additional commits will be made to extend this capability, but
this commit adds two new access.conf keywords: FORWARD_ALL and
DISABLE_DNAT. These are used in conjunction to add ACCEPT rules for all
ports/protocols in the FORWARD chain, and also disable DNAT rules at the
same time. Then, by buildling the SNAT chain to provide translation for
an internal network (where an SPA cliet is located), but DROP all
forwarded traffic by default at the same time, SPA can be used to gain
access to the internet. So, this would allow, say, an RFC 1918 internal
network to have IP's assigned via DHCP but they wouldn't be able to
access the internet before sending a SPA packet to the gateway. This
scenario was suggested by spartan1833 to the fwknop list and tracked via
github issue 131.

Additional commits will be made to fully support this feature.
This commit is contained in:
Michael Rash 2015-01-17 08:38:32 -05:00
parent d148fb091a
commit 6b7a3bbdae
5 changed files with 67 additions and 24 deletions

View File

@ -139,6 +139,8 @@ enum {
#define FKO_DEFAULT_PORT 62201
#define DEFAULT_NAT_PORT 55000
#define MIN_HIGH_PORT 10000 /* sensible minimum for SPA dest port */
#define ANY_PORT 0 /* used as a wildcard */
#define ANY_PROTO 0 /* used as a wildcard */
#define MAX_SERVER_STR_LEN 50
#define MAX_ICMP_TYPE 40
#define MAX_ICMP_CODE 15

View File

@ -1134,10 +1134,11 @@ acc_data_is_valid(struct passwd *user_pw, acc_stanza_t * const acc)
}
}
if((acc->force_snat == 1 || acc->force_masquerade == 1) && acc->force_nat == 0)
if((acc->force_snat == 1 || acc->force_masquerade == 1)
&& acc->force_nat == 0 && acc->disable_dnat == 0)
{
log_msg(LOG_ERR,
"[*] FORCE_SNAT/FORCE_MASQUERADE implies FORCE_NAT must also be used for access stanza source: '%s'",
"[*] FORCE_SNAT/FORCE_MASQUERADE implies FORCE_NAT or DISABLE_DNAT must also be used for stanza source: '%s'",
acc->source
);
return(0);
@ -1707,6 +1708,14 @@ parse_access_file(fko_srv_options_t *opts)
add_acc_bool(&(curr_acc->force_masquerade), val);
add_acc_bool(&(curr_acc->force_snat), val);
}
else if(CONF_VAR_IS(var, "DISABLE_DNAT"))
{
add_acc_bool(&(curr_acc->disable_dnat), val);
}
else if(CONF_VAR_IS(var, "FORWARD_ALL"))
{
add_acc_bool(&(curr_acc->forward_all), val);
}
else
{
log_msg(LOG_ERR,
@ -1941,6 +1950,8 @@ dump_access_list(const fko_srv_options_t *opts)
" FORCE_NAT (port): %d\n"
" FORCE_SNAT (ip): %s\n"
" FORCE_MASQUERADE: %s\n"
" DISABLE_DNAT: %s\n"
" FORWARD_ALL: %s\n"
" ACCESS_EXPIRE: %s" /* asctime() adds a newline */
" GPG_HOME_DIR: %s\n"
" GPG_EXE: %s\n"
@ -1972,6 +1983,8 @@ dump_access_list(const fko_srv_options_t *opts)
acc->force_nat ? acc->force_nat_port : 0,
acc->force_snat ? acc->force_snat_ip : "<not set>",
acc->force_masquerade ? "Yes" : "No",
acc->disable_dnat ? "Yes" : "No",
acc->forward_all ? "Yes" : "No",
(acc->access_expire_time > 0) ? asctime(localtime(&acc->access_expire_time)) : "<not set>\n",
(acc->gpg_home_dir == NULL) ? "<not set>" : acc->gpg_home_dir,
(acc->gpg_exe == NULL) ? "<not set>" : acc->gpg_exe,

View File

@ -122,11 +122,14 @@ rule_exists_no_chk_support(const fko_srv_options_t * const opts,
* primary search method
*/
if(search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, exp_ts_search, &pid_status, opts)
&& search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, proto_search, &pid_status, opts)
&& (proto == ANY_PROTO) ? 1 :
search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, proto_search, &pid_status, opts)
&& search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, srcip_search, &pid_status, opts)
&& (dstip == NULL) ? 1 : search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, dstip_search, &pid_status, opts)
&& (dstip == NULL) ? 1 :
search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, dstip_search, &pid_status, opts)
&& search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, target_search, &pid_status, opts)
&& search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, port_search, &pid_status, opts))
&& (port == ANY_PORT) ? 1 :
search_extcmd(cmd_buf, WANT_STDERR, NO_TIMEOUT, port_search, &pid_status, opts))
rule_exists = 1;
if(rule_exists)
@ -179,7 +182,7 @@ 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 srcip,
const char * const dstip, const unsigned int port,
const char * const dstip, const unsigned int port,
const unsigned int exp_ts)
{
int rule_exists = 0;
@ -187,7 +190,8 @@ rule_exists(const fko_srv_options_t * const opts,
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, srcip, (opts->fw_config->use_destination ? dstip : NULL), 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",
@ -944,7 +948,8 @@ create_rule(const fko_srv_options_t * const opts,
zero_cmd_buffers();
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s", opts->fw_config->fw_command, fw_chain, fw_rule);
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s -A %s %s",
opts->fw_config->fw_command, fw_chain, fw_rule);
res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR,
NO_TIMEOUT, &pid_status, opts);
@ -955,7 +960,8 @@ create_rule(const fko_srv_options_t * const opts,
if(EXTCMD_IS_SUCCESS(res))
{
log_msg(LOG_DEBUG, "create_rule() Rule: '%s' added to %s", fw_rule, fw_chain);
log_msg(LOG_DEBUG, "create_rule() Rule: '%s' added to %s",
fw_rule, fw_chain);
res = 1;
}
else
@ -1005,7 +1011,8 @@ ipt_rule(const fko_srv_options_t * const opts,
if(create_rule(opts, chain->to_chain, rule_buf))
{
log_msg(LOG_INFO, "Added %s rule to %s for %s -> %s %s, expires at %u",
msg, chain->to_chain, srcip, dstip, access_msg, exp_ts
msg, chain->to_chain, srcip, (dstip == NULL) ? IPT_ANY_IP : dstip,
access_msg, exp_ts
);
chain->active_rules++;
@ -1136,7 +1143,8 @@ process_spa_request(const fko_srv_options_t * const opts,
return res;
}
nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT,
NO_EXIT_UPON_ERR, &is_err);
if(is_err != FKO_SUCCESS)
{
log_msg(LOG_INFO, "Invalid NAT port in SPA message");
@ -1156,15 +1164,35 @@ process_spa_request(const fko_srv_options_t * const opts,
}
else if(strlen(fwd_chain->to_chain))
{
/* Make our FORWARD and NAT rules, and make sure the
* required chain and jump rule exists
*/
ipt_rule(opts, NULL, IPT_FWD_RULE_ARGS, spadat->use_src_ip,
nat_ip, fst_proto, nat_port, fwd_chain, exp_ts, now, "FORWARD",
spadat->spa_message_remain);
if(acc->forward_all)
{
memset(rule_buf, 0, CMD_BUFSIZE);
snprintf(rule_buf, CMD_BUFSIZE-1, IPT_FWD_ALL_RULE_ARGS,
fwd_chain->table,
spadat->use_src_ip,
exp_ts,
fwd_chain->target
);
/* Make a global ACCEPT rule for all ports/protocols
*/
ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip,
NULL, ANY_PROTO, ANY_PORT, fwd_chain, exp_ts, now,
"FORWARD ALL", "*/*");
}
else
{
/* Make our FORWARD and NAT access rules
*/
ipt_rule(opts, NULL, IPT_FWD_RULE_ARGS, spadat->use_src_ip,
nat_ip, fst_proto, nat_port, fwd_chain, exp_ts, now,
"FORWARD", spadat->spa_message_remain);
}
}
if(strlen(dnat_chain->to_chain))
if(!acc->disable_dnat && strlen(dnat_chain->to_chain))
{
memset(rule_buf, 0, CMD_BUFSIZE);
@ -1180,7 +1208,7 @@ process_spa_request(const fko_srv_options_t * const opts,
nat_port
);
ipt_rule(opts, rule_buf, IPT_DNAT_RULE_ARGS, spadat->use_src_ip,
ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip,
(fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP),
fst_proto, fst_port, dnat_chain, exp_ts, now, "DNAT",
spadat->spa_message_remain);
@ -1235,7 +1263,7 @@ process_spa_request(const fko_srv_options_t * const opts,
snat_target
);
ipt_rule(opts, rule_buf, IPT_SNAT_RULE_ARGS, spadat->use_src_ip,
ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip,
NULL, fst_proto, nat_port, snat_chain, exp_ts, now, "SNAT",
spadat->spa_message_remain);
}

View File

@ -45,6 +45,7 @@
#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_FWD_ALL_RULE_ARGS "-t %s -s %s -m comment --comment " EXPIRE_COMMENT_PREFIX "%u -j %s" 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

View File

@ -389,15 +389,14 @@ typedef struct acc_stanza
int expired;
int encryption_mode;
/* DNAT parameters
/* NAT parameters
*/
unsigned char force_nat;
char *force_nat_ip;
char *force_nat_proto;
unsigned int force_nat_port;
/* SNAT parameters
*/
unsigned char disable_dnat;
unsigned char forward_all;
unsigned char force_snat;
char *force_snat_ip;
unsigned char force_masquerade;