From 579ec77698efeccbc933d7bcfbd0431577a1ed20 Mon Sep 17 00:00:00 2001 From: Damien Stuart Date: Tue, 15 Jun 2010 02:50:17 +0000 Subject: [PATCH] Added support for FWKNOP_OUTPUT_ACCESS and NAT_ACCESS modes (still needs testing and tweaking). git-svn-id: file:///home/mbr/svn/fwknop/trunk@216 510a4753-2344-4c79-9c09-4d669213fbeb --- server/access.conf | 11 +- server/fw_util.c | 234 ++++++++++++++++++++++++++++++++++++---- server/fw_util.h | 3 + server/fwknopd_common.h | 6 +- server/incoming_spa.c | 143 ++++++++++++------------ 5 files changed, 298 insertions(+), 99 deletions(-) diff --git a/server/access.conf b/server/access.conf index 890dba68..38d41b95 100644 --- a/server/access.conf +++ b/server/access.conf @@ -73,7 +73,16 @@ # # This specifies whether or not fwknopd will accept complete commands that # are contained within a SPA packet. Any such command will be executed as -# root by the fwknopd server. If not set here, the default is "N". +# user specified using the CMD_EXEC_USER parameter by the fwknopd server. +# If not set here, the default is "N". +# + +# CMD_EXEC_USER: ; +# +# This specifies the user that will execute commands contained within a SPA +# packet. If not specified, fwknopd will execute it as the user it is +# running as (most likely root). Setting this to a non-root user is highly +# recommended. # # CMD_REGEX: ; **NOT YET SUPPORTED** diff --git a/server/fw_util.c b/server/fw_util.c index 324cf994..51f06632 100644 --- a/server/fw_util.c +++ b/server/fw_util.c @@ -137,7 +137,7 @@ delete_all_chains(void) char cmd_buf[CMD_BUFSIZE] = {0}; char err[CMD_BUFSIZE] = {0}; - for(i=0; i<(NUM_FWKNOP_CHAIN_TYPES-1); i++) + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES-1); i++) { if(fwc.chain[i].target[0] == '\0') continue; @@ -193,7 +193,7 @@ create_fw_chains(void) char cmd_buf[CMD_BUFSIZE] = {0}; char err[CMD_BUFSIZE] = {0}; - for(i=0; i<(NUM_FWKNOP_CHAIN_TYPES-1); i++) + for(i=0; i<(NUM_FWKNOP_ACCESS_TYPES-1); i++) { if(fwc.chain[i].target[0] == '\0') continue; @@ -339,20 +339,51 @@ fw_initialize(fko_srv_options_t *opts) exit(EXIT_FAILURE); } - if(opts->config[CONF_IPT_OUTPUT_ACCESS] != NULL) + /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_IPT_OUTPUT_ACCESS be Y + */ + if(opts->config[CONF_ENABLE_IPT_OUTPUT] != NULL + && (strncasecmp(opts->config[CONF_ENABLE_IPT_OUTPUT], "Y", 1)==0) + && opts->config[CONF_IPT_OUTPUT_ACCESS] != NULL) set_fw_chain_conf(IPT_OUTPUT_ACCESS, opts->config[CONF_IPT_OUTPUT_ACCESS]); - if(opts->config[CONF_IPT_FORWARD_ACCESS] != NULL) - set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]); + /* The remaining access chains require ENABLE_IPT_FORWARDING = Y + */ + if(opts->config[CONF_ENABLE_IPT_FORWARDING] != NULL + && (strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)==0)) + { - if(opts->config[CONF_IPT_DNAT_ACCESS] != NULL) - set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]); + if(opts->config[CONF_IPT_FORWARD_ACCESS] != NULL) + set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]); - if(opts->config[CONF_IPT_SNAT_ACCESS] != NULL) - set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]); + if(opts->config[CONF_IPT_DNAT_ACCESS] != NULL) + set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]); - if(opts->config[CONF_IPT_MASQUERADE_ACCESS] != NULL) - set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]); + /* SNAT (whichever mode) requires ENABLE_IPT_SNAT = Y + */ + if(opts->config[CONF_ENABLE_IPT_SNAT] != NULL + && (strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1)==0)) + { + /* If an SNAT_TRANSLATE_IP is specified use the SNAT_ACCESS mode. + * Otherwise, use MASQUERADE_ACCESS. + * + * XXX: --DSS: Not sure if using the TRANSLATE_IP parameter as + * the determining factor is the best why to handle + * this. + * + */ + if(opts->config[CONF_ENABLE_IPT_SNAT] != NULL + && strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "_CHANGE_ME_", 11)!=0) + { + if(opts->config[CONF_IPT_SNAT_ACCESS] != NULL) + set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]); + } + else + { + if(opts->config[CONF_IPT_MASQUERADE_ACCESS] != NULL) + set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]); + } + } + } /* Let us find it via our opts struct as well. */ @@ -388,13 +419,22 @@ process_access_request(fko_srv_options_t *opts, spa_data_t *spadat) { char cmd_buf[CMD_BUFSIZE] = {0}; char err[CMD_BUFSIZE] = {0}; + char nat_ip[16] = {0}; + char *ndx; + + unsigned int nat_port = 0;; + acc_port_list_t *port_list = NULL; acc_port_list_t *ple; unsigned int cproto; unsigned int cport; - struct fw_chain *chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]); + struct fw_chain *in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]); + struct fw_chain *out_chain = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]); + struct fw_chain *fwd_chain = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]); + struct fw_chain *dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]); + struct fw_chain *snat_chain; // We assign this later (when we get it done) int status, res; time_t now, exp_ts; @@ -406,54 +446,202 @@ process_access_request(fko_srv_options_t *opts, spa_data_t *spadat) /* Create an access command for each proto/port for the source ip. */ ple = port_list; + + cproto = ple->proto; + cport = ple->port; + while(ple != NULL) { time(&now); exp_ts = now + spadat->fw_access_timeout; + memset(cmd_buf, 0x0, CMD_BUFSIZE); + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_RULE_ARGS, opts->fw_config->fw_command, - chain->table, - chain->to_chain, + in_chain->table, + in_chain->to_chain, ple->proto, spadat->use_src_ip, ple->port, exp_ts, - chain->target + in_chain->target ); //--DSS tmp -//fprintf(stderr, "ADD CMD: %s", cmd_buf); +//fprintf(stderr, "ADD CMD: %s\n", cmd_buf); res = run_extcmd(cmd_buf, NULL, err, 0, CMD_BUFSIZE, &status); if(EXTCMD_IS_SUCCESS(res)) { log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u", - chain->to_chain, spadat->use_src_ip, + in_chain->to_chain, spadat->use_src_ip, spadat->spa_message_remain, exp_ts ); - chain->active_rules++; + in_chain->active_rules++; /* Reset the next expected expire time for this chain if it * is warranted. */ - if(chain->next_expire < now || exp_ts < chain->next_expire) - chain->next_expire = exp_ts; + if(in_chain->next_expire < now || exp_ts < in_chain->next_expire) + in_chain->next_expire = exp_ts; } else parse_extcmd_error(res, status, err); + /* If we have to make an corresponding OUTPUT rule if out_chain target + * is not NULL. + */ + if(out_chain->to_chain != NULL && strlen(out_chain->to_chain)) + { + memset(cmd_buf, 0x0, CMD_BUFSIZE); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_OUT_RULE_ARGS, + opts->fw_config->fw_command, + out_chain->table, + out_chain->to_chain, + ple->proto, + spadat->use_src_ip, + ple->port, + exp_ts, + out_chain->target + ); + +//--DSS tmp +//fprintf(stderr, "ADD OUTPUT CMD: %s\n", cmd_buf); + res = run_extcmd(cmd_buf, NULL, err, 0, CMD_BUFSIZE, &status); + if(EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_INFO, "Added OUTPUT Rule to %s for %s, %s expires at %u", + out_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + out_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(out_chain->next_expire < now || exp_ts < out_chain->next_expire) + out_chain->next_expire = exp_ts; + } + else + parse_extcmd_error(res, status, err); + + } + ple = ple->next; } - - /* Done with the port list. + /* Done with the port list for access rules. */ free_acc_port_list(port_list); + /* Now see if we have NAT rules to create... + */ + if( spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG + || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG + || spadat->message_type == FKO_NAT_ACCESS_MSG + || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG ) + { + /* Parse out the NAT IP and Port components. + */ + ndx = strchr(spadat->nat_access, ','); + if(ndx != NULL) + { + strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1); + nat_port = atoi(ndx+1); + } + +// --DSS temp +//fprintf(stderr, "NAT IP: '%s', NAT PORT: '%i'\n", nat_ip, nat_port); + + /* Make our FORWARD and NAT rules + */ + if(fwd_chain->to_chain != NULL && strlen(fwd_chain->to_chain)) + { + memset(cmd_buf, 0x0, CMD_BUFSIZE); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_FWD_RULE_ARGS, + opts->fw_config->fw_command, + fwd_chain->table, + fwd_chain->to_chain, + cproto, + spadat->use_src_ip, + nat_ip, + nat_port, + exp_ts, + fwd_chain->target + ); + +//--DSS tmp +//fprintf(stderr, "ADD OUTPUT CMD: %s\n", cmd_buf); + res = run_extcmd(cmd_buf, NULL, err, 0, CMD_BUFSIZE, &status); + if(EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_INFO, "Added FORWARD Rule to %s for %s, %s expires at %u", + fwd_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + fwd_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(fwd_chain->next_expire < now || exp_ts < fwd_chain->next_expire) + fwd_chain->next_expire = exp_ts; + } + else + parse_extcmd_error(res, status, err); + } + + if(dnat_chain->to_chain != NULL && strlen(dnat_chain->to_chain)) + { + memset(cmd_buf, 0x0, CMD_BUFSIZE); + + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_DNAT_RULE_ARGS, + opts->fw_config->fw_command, + dnat_chain->table, + dnat_chain->to_chain, + cproto, + spadat->use_src_ip, + cport, + exp_ts, + dnat_chain->target, + nat_ip, + nat_port + ); + +//--DSS tmp +//fprintf(stderr, "ADD DNAT CMD: %s\n", cmd_buf); + res = run_extcmd(cmd_buf, NULL, err, 0, CMD_BUFSIZE, &status); + if(EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_INFO, "Added DNAT Rule to %s for %s, %s expires at %u", + dnat_chain->to_chain, spadat->use_src_ip, + spadat->spa_message_remain, exp_ts + ); + + dnat_chain->active_rules++; + + /* Reset the next expected expire time for this chain if it + * is warranted. + */ + if(dnat_chain->next_expire < now || exp_ts < dnat_chain->next_expire) + dnat_chain->next_expire = exp_ts; + } + else + parse_extcmd_error(res, status, err); + } + } + return(res); } +/* Iterate over the configure firewall access chains and purge expired + * firewall rules. +*/ void check_firewall_rules(fko_srv_options_t *opts) { @@ -473,7 +661,7 @@ check_firewall_rules(fko_srv_options_t *opts) /* Iterate over each chain and look for active rules to delete. */ - for(i = 0; i < NUM_FWKNOP_CHAIN_TYPES; i++) + for(i = 0; i < NUM_FWKNOP_ACCESS_TYPES; i++) { /* Just in case we somehow lose track and fall out-of-whack. */ diff --git a/server/fw_util.h b/server/fw_util.h index c5ac5347..5e05406b 100644 --- a/server/fw_util.h +++ b/server/fw_util.h @@ -34,6 +34,9 @@ /* iptables command args */ #define IPT_ADD_RULE_ARGS "-t %s -A %s -p %i -s %s --dport %i -m comment --comment _exp_%u -j %s" +#define IPT_ADD_OUT_RULE_ARGS "-t %s -A %s -p %i -d %s --sport %i -m comment --comment _exp_%u -j %s" +#define IPT_ADD_FWD_RULE_ARGS "-t %s -A %s -p %i -s %s -d %s --dport %i -m comment --comment _exp_%u -j %s" +#define IPT_ADD_DNAT_RULE_ARGS "-t %s -A %s -p %i -s %s --dport %i -m comment --comment _exp_%u -j %s --to-destination %s:%i" #define IPT_DEL_RULE_ARGS "-t %s -D %s %i" #define IPT_NEW_CHAIN_ARGS "-t %s -N %s" #define IPT_FLUSH_CHAIN_ARGS "-t %s -F %s" diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 749eafb8..0d1ed1ae 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -340,7 +340,7 @@ enum { IPT_DNAT_ACCESS, IPT_SNAT_ACCESS, IPT_MASQUERADE_ACCESS, - NUM_FWKNOP_CHAIN_TYPES /* Leave this entry last */ + NUM_FWKNOP_ACCESS_TYPES /* Leave this entry last */ }; #define FW_CHAIN_DIR_SRC_STR "src" @@ -376,7 +376,7 @@ struct fw_chain { #define FW_NUM_CHAIN_FIELDS 7 struct fw_config { - struct fw_chain chain[NUM_FWKNOP_CHAIN_TYPES]; + struct fw_chain chain[NUM_FWKNOP_ACCESS_TYPES]; char fw_command[MAX_PATH_LEN]; }; @@ -400,7 +400,7 @@ typedef struct spa_data char *spa_message; char spa_message_src_ip[16]; char pkt_source_ip[16]; - char spa_message_remain[1024]; /* --DSS arbitrary bounds */ + char spa_message_remain[1024]; /* --DSS FIXME: arbitrary bounds */ char *nat_access; char *server_auth; unsigned int client_timeout; diff --git a/server/incoming_spa.c b/server/incoming_spa.c index b182bebf..b670a315 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -281,7 +281,7 @@ incoming_spa(fko_srv_options_t *opts) goto clean_and_bail; } - strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark - spadat.spa_message) + 1); + strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); strlcpy(spadat.spa_message_remain, spa_ip_demark+1, 1024); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it @@ -319,87 +319,86 @@ incoming_spa(fko_srv_options_t *opts) } } - /* Take action based on SPA message type. + /* Take action based on SPA message type. */ + + /* Command messages. */ - switch(spadat.message_type) + if(spadat.message_type == FKO_COMMAND_MSG) { - /* Command messages. - */ - case FKO_COMMAND_MSG: - if(!acc->enable_cmd_exec) - { - log_msg(LOG_WARNING|LOG_STDERR, - "SPA Command message are not allowed in the current configuration." - ); - res = SPA_MSG_ACCESS_DENIED; - } - else - { - /* --DSS TODO: Finish Me */ - log_msg(LOG_WARNING|LOG_STDERR, - "SPA Command message are not yet supported." - ); - res = SPA_MSG_NOT_SUPPORTED; - } - break; - - /* NAT access messages. - */ - case FKO_NAT_ACCESS_MSG: - case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG: + if(!acc->enable_cmd_exec) + { log_msg(LOG_WARNING|LOG_STDERR, - "SPA NAT access messages are not yet supported." + "SPA Command message are not allowed in the current configuration." + ); + res = SPA_MSG_ACCESS_DENIED; + } + else + { + /* --DSS TODO: Finish Me */ + log_msg(LOG_WARNING|LOG_STDERR, + "SPA Command message are not yet supported." ); res = SPA_MSG_NOT_SUPPORTED; - /* --DSS TODO: Finish Me */ - break; + } - /* Local NAT access messages. - */ - case FKO_LOCAL_NAT_ACCESS_MSG: - case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG: - log_msg(LOG_WARNING|LOG_STDERR, - "SPA Local NAT access messages are not yet supported." - ); - res = SPA_MSG_NOT_SUPPORTED; - /* --DSS TODO: Finish Me */ - break; - - /* Standard access messages. - */ - case FKO_ACCESS_MSG: - case FKO_CLIENT_TIMEOUT_ACCESS_MSG: - /* Check access against restrict_ports and open_ports. - */ - res = acc_check_port_access(acc, spadat.spa_message_remain); - - if(!res) - { - log_msg(LOG_WARNING|LOG_STDERR, - "One or more requested protocol/ports was denied per access.conf." - ); - res = SPA_MSG_ACCESS_DENIED; - } - else - { - /* Process the access message. - */ - /* --DSS temp - log_msg(LOG_WARNING|LOG_STDERR, - "<<<< This SPA access msg would be %s >>>>", - res ? "allowed":"DENIED due to port restictions" - ); - res = SPA_MSG_NOT_SUPPORTED; - */ - res = process_access_request(opts, &spadat); - } - break; + goto clean_and_bail; } - /* Send to the firewall rule processor. + /* From this point forward, we have some kind of access message. So + * we first see if access is allowed by checking access against + * restrict_ports and open_ports. + * + * --DSS TODO: We should add BLACKLIST support here as well. */ - // TODO: Finish me + if(! acc_check_port_access(acc, spadat.spa_message_remain)) + { + log_msg(LOG_WARNING|LOG_STDERR, + "One or more requested protocol/ports was denied per access.conf." + ); + res = SPA_MSG_ACCESS_DENIED; + + goto clean_and_bail; + } + + /* If we are here we will at least need to have the base access. So we + * can go ahead an generate that now. + */ + res = process_access_request(opts, &spadat); + +#if 0 + if(res != FKO_SUCCESS) + goto clean_and_bail; + + /* Now see if we are looking for a local NAT access of some sort. + */ + if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG + || spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) + { + log_msg(LOG_WARNING|LOG_STDERR, + "SPA Local NAT access messages are not yet supported." + ); + + /* --DSS TODO: Finish Me */ + res = SPA_MSG_NOT_SUPPORTED; + + goto clean_and_bail; + } + + /* NAT access messages. + */ + if(spadat.message_type == FKO_NAT_ACCESS_MSG + || spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG) + { + log_msg(LOG_WARNING|LOG_STDERR, + "SPA NAT access messages are not yet supported." + ); + + res = SPA_MSG_NOT_SUPPORTED; + + /* --DSS TODO: Finish Me */ + } +#endif clean_and_bail: if(ctx != NULL)