/** * @brief Check a string to find out if it is built as 'hostname,port' * * This function check if we can extract an hostname and a port from the string. * If yes, we return 1, and both the hostname buffer and the port number are set * accordingly. * * We could have used sscanf() here with a template "%[^,],%u", but this way we * do not limit the size of the value copy in the hostname destination buffer. * Limiting the string in the sscanf() can be done but would prevent any easy change * for the hostname buffer size. * * @param str String to parse. * @param hostname Buffer where to store the hostname value read from @str. * @param hostname_bufsize Hostname buffer size. * @param port Value of the port read from @str. * * @return 1 if the string is built as 'hostname,port', 0 otherwise. */ static int is_hostname_str_with_port(const char *str, char *hostname, size_t hostname_bufsize, int *port) { int valid = 0; /* Result of the function */ char buf[MAX_LINE_LEN] = {0}; /* Copy of the buffer eg. "hostname,port" */ char *h; /* Pointer on the hostname string */ char *p; /* Ponter on the port string */ memset(hostname, 0, hostname_bufsize); *port = 0; /* Replace the comma in the string with a NULL char to split the * buffer in two strings (hostname and port) */ strlcpy(buf, str, sizeof(buf)); p = strchr(buf, ','); if(p != NULL) { *p++ = 0; h = buf; *port = atoi(p); /* If the string does not match an ipv4 or ipv6 address we assume this * is an hostname. We make sure the port is in the good range too */ if ( (is_valid_ipv4_addr(buf) == 0) && (is_ipv6_str(buf) == 0) && ((*port > 0) && (*port < 65536)) ) { strlcpy(hostname, h, hostname_bufsize); valid = 1; } /* The port is out of range or the ip is an ipv6 or ipv4 address */ else; } /* No port found in the string, let's skip */ else; return valid; }
/* Process the SPA packet data */ void incoming_spa(fko_srv_options_t *opts) { /* Always a good idea to initialize ctx to null if it will be used * repeatedly (especially when using fko_new_with_data()). */ fko_ctx_t ctx = NULL; char *spa_ip_demark, *raw_digest = NULL; int res, enc_type, stanza_num=0; int added_replay_digest = 0; int is_err, cmd_exec_success = 0, attempted_decrypt = 0; int conf_pkt_age = 0; char dump_buf[CTX_DUMP_BUFSIZE]; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Loop through all access stanzas looking for a match */ acc_stanza_t *acc = opts->acc_stanzas; 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 * try to eliminate obvious non-spa packets). */ if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest)) return; if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { conf_pkt_age = strtol_wrapper(opts->config[CONF_MAX_SPA_PACKET_AGE], 0, RCHK_MAX_SPA_PACKET_AGE, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] [%s] invalid MAX_SPA_PACKET_AGE", spadat.pkt_source_ip); return; } } /* Now that we know there is a matching access.conf stanza and the * incoming SPA packet is not a replay, see if we should grant any * access */ while(acc) { res = FKO_SUCCESS; cmd_exec_success = 0; attempted_decrypt = 0; stanza_num++; /* Start access loop with a clean FKO context */ if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } /* Check for a match for the SPA source and destination IP and the access stanza */ if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num)) { acc = acc->next; continue; } log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match", stanza_num, spadat.pkt_source_ip); log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data); /* Make sure this access stanza has not expired */ if(! check_stanza_expiration(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Get encryption type and try its decoding routine first (if the key * for that type is set) */ enc_type = fko_encryption_type((char *)spa_pkt->packet_data); if(! handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, &cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt, enc_type, stanza_num, res)) { acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ if(! add_replay_cache(opts, acc, &spadat, raw_digest, &added_replay_digest, stanza_num, &res)) { acc = acc->next; continue; } /* At this point the SPA data is authenticated via the HMAC (if used * for now). Next we need to see if it meets our access criteria which * the server imposes regardless of the content of the SPA packet. */ log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):", spadat.pkt_source_ip, stanza_num, res); res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_DEBUG, "%s", dump_buf); else log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty, * then we need to make sure this incoming message is signer ID matches * an entry in the list. */ if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res)) { acc = acc->next; continue; } /* Populate our spa data struct for future reference. */ res = get_spa_data_fields(ctx, &spadat); if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } /* Figure out what our timeout will be. If it is specified in the SPA * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the * access.conf file (if there is one). Otherwise use the default. */ set_timeout(acc, &spadat); /* Check packet age if so configured. */ if(! check_pkt_age(opts, &spadat, stanza_num, conf_pkt_age)) { acc = acc->next; continue; } /* At this point, we have enough to check the embedded (or packet source) * IP address against the defined access rights. We start by splitting * the spa msg source IP from the remainder of the message. */ spa_ip_demark = strchr(spadat.spa_message, ','); if(spa_ip_demark == NULL) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num); break; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); if(! is_valid_ipv4_addr(spadat.spa_message_src_ip)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); break; } strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(! check_src_access(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ if(! check_username(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Take action based on SPA message type. */ if(! check_nat_access_types(opts, acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Command messages. */ if(spadat.message_type == FKO_COMMAND_MSG) { if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res)) { /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } else { acc = acc->next; continue; } } /* 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. */ if(! check_port_proto(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we process the SPA request and break out of the * access stanza loop (first valid access stanza stops us looking * for others). */ if(opts->test) /* no firewall changes in --test mode */ { log_msg(LOG_WARNING, "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { process_spa_request(opts, acc, &spadat); } /* If we made it here, then the SPA packet was processed according * to a matching access.conf stanza, so we're done with this packet. */ break; } if (raw_digest != NULL) free(raw_digest); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } return; }
/* Process the SPA packet data */ void incoming_spa(fko_srv_options_t *opts) { /* Always a good idea to initialize ctx to null if it will be used * repeatedly (especially when using fko_new_with_data()). */ fko_ctx_t ctx = NULL; char *spa_ip_demark, *raw_digest = NULL; int res, enc_type, stanza_num=0; int added_replay_digest = 0; int cmd_exec_success = 0, attempted_decrypt = 0; char dump_buf[CTX_DUMP_BUFSIZE]; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Loop through all access stanzas looking for a match */ //²éÕÒËùÓеÄstanzas£¬Æ¥ÅäÏàÓ¦Ïî¡£ acc_stanza_t *acc = opts->acc_stanzas; 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 * try to eliminate obvious non-spa packets). */ //ÔÚÕâÀÎÒÃÇÒªÑéÖ¤ºÍÔ¤´¦ÀíSPAÊý¾Ý£¬ÌÞ³ýÃ÷ÏÔ²»ÊÇSPAµÄÊý¾Ý°ü¡£ //ͨ¹ý±È¶ÔÕªÒª¼ì²éÊý¾ÝÍêÕûÐÔºÍÊÇ·ñÊܵ½ÖطŹ¥»÷¡£ //¼ÆËãctx->encrypted_msgµÄSHA256ÕªÒªÊý¾Ý£¬´æÔÚraw_digest¡£ if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest)) return; /* Now that we know there is a matching access.conf stanza and the * incoming SPA packet is not a replay, see if we should grant any * access */ while(acc) { res = FKO_SUCCESS; cmd_exec_success = 0; attempted_decrypt = 0; stanza_num++; /* Start access loop with a clean FKO context */ //¶Ôÿһ¸östanza½øÐÐÆ¥Å䣬ֱµ½Æ¥Åä³É¹¦ÎªÖ¹¡£ if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } /* Check for a match for the SPA source and destination IP and the access stanza */ //Æ¥ÅästanzaÖеÄÔ´µØÖ·ºÍÄ¿µÄµØÖ·¡£ if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num)) { acc = acc->next; continue; } log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match", stanza_num, spadat.pkt_source_ip); log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data); /* Make sure this access stanza has not expired */ //È·±£Õâ¸östanzaûÓйýÆÚ¡£ if(! check_stanza_expiration(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Get encryption type and try its decoding routine first (if the key * for that type is set) */ //¼ì²â´Ë¶ÎÃÜÎĵļÓÃÜÀàÐÍ¡£ enc_type = fko_encryption_type((char *)spa_pkt->packet_data); //»¹Ô¼ÓÑÎÃÜÎÄ£¬Í¨¹ýHMACժҪУÑéÊý¾Ý°üµÄÍêÕûÐÔ£¬½âÃÜencrypted_msg¡£ if(acc->use_rijndael) handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, &cmd_exec_success, enc_type, stanza_num, &res); if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt, cmd_exec_success, enc_type, stanza_num, &res)) { acc = acc->next; continue; } if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt, enc_type, stanza_num, res)) { acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ //¼ì²âÖطŹ¥»÷¡£ if(! add_replay_cache(opts, acc, &spadat, raw_digest, &added_replay_digest, stanza_num, &res)) { acc = acc->next; continue; } /* At this point the SPA data is authenticated via the HMAC (if used * for now). Next we need to see if it meets our access criteria which * the server imposes regardless of the content of the SPA packet. */ log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):", spadat.pkt_source_ip, stanza_num, res); res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_DEBUG, "%s", dump_buf); else log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty, * then we need to make sure this incoming message is signer ID matches * an entry in the list. */ if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res)) { acc = acc->next; continue; } /* Populate our spa data struct for future reference. */ //ÏòspadatÖÐÌî³ä×ֶΡ£ res = get_spa_data_fields(ctx, &spadat); if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } /* Figure out what our timeout will be. If it is specified in the SPA * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the * access.conf file (if there is one). Otherwise use the default. */ //²é¿´ÔÚSPAÖÐÓÐûÓÐÉèÖó¬Ê±×ֶΣ¬·ñÔò¾ÍÓÃaccess.confÖÐÉèÖõÄ×ֶΡ£ set_timeout(acc, &spadat); /* Check packet age if so configured. */ //²é¿´Êý¾Ý°üÖеÄʱ¼ä´ÁÊÇ·ñ¹ýʱ¡£ if(! check_pkt_age(opts, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we have enough to check the embedded (or packet source) * IP address against the defined access rights. We start by splitting * the spa msg source IP from the remainder of the message. */ spa_ip_demark = strchr(spadat.spa_message, ','); if(spa_ip_demark == NULL) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num); break; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); if(! is_valid_ipv4_addr(spadat.spa_message_src_ip)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); break; } strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(! check_src_access(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ //Èç¹ûREQUIRE_USERNAME ×ֶα»ÉèÖã¬È·±£Óû§ÃûÔÚSPAÖпɱ»Æ¥Åä¡£ if(! check_username(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Take action based on SPA message type. */ if(! check_nat_access_types(opts, acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* Command messages. */ if(acc->cmd_cycle_open != NULL) { if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res)) break; /* successfully processed a matching access stanza */ else { acc = acc->next; continue; } } else if(spadat.message_type == FKO_COMMAND_MSG) { if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res)) { /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } else { acc = acc->next; continue; } } /* 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. */ if(! check_port_proto(acc, &spadat, stanza_num)) { acc = acc->next; continue; } /* At this point, we process the SPA request and break out of the * access stanza loop (first valid access stanza stops us looking * for others). */ if(opts->test) /* no firewall changes in --test mode */ { log_msg(LOG_WARNING, "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { if(acc->cmd_cycle_open != NULL) { //Ö´ÐÐiptablesµÄCMD¡£ if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res)) break; /* successfully processed a matching access stanza */ else { acc = acc->next; continue; } } else { process_spa_request(opts, acc, &spadat); } } /* If we made it here, then the SPA packet was processed according * to a matching access.conf stanza, so we're done with this packet. */ break; } if (raw_digest != NULL) free(raw_digest); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } return; }
/* Rule Processing - Create an access request... */ int process_spa_request(const fko_srv_options_t * const opts, const acc_stanza_t * const acc, spa_data_t * const spadat) { char nat_ip[MAX_IPV4_STR_LEN] = {0}; unsigned int nat_port = 0; unsigned int fst_proto; unsigned int fst_port; struct fw_chain * const in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]); struct fw_chain * const out_chain = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]); struct fw_chain * const fwd_chain = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]); struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]); acc_port_list_t *port_list = NULL; acc_port_list_t *ple = NULL; char *ndx = NULL; int res = 0, is_err; time_t now; unsigned int exp_ts; /* Parse and expand our access message. */ if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1) { /* technically we would already have exited with an error if there were * any memory allocation errors (see the add_port_list() function), but * for completeness... */ free_acc_port_list(port_list); return res; } /* Start at the top of the proto-port list... */ ple = port_list; /* Remember the first proto/port combo in case we need them * for NAT access requests. */ fst_proto = ple->proto; fst_port = ple->port; /* Set our expire time value. */ time(&now); exp_ts = now + spadat->fw_access_timeout; /* deal with SPA packets that themselves request a NAT operation */ 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) { ndx = strchr(spadat->nat_access, ','); if(ndx != NULL) { strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1); if (! is_valid_ipv4_addr(nat_ip)) { log_msg(LOG_INFO, "Invalid NAT IP in SPA message"); free_acc_port_list(port_list); return res; } 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"); free_acc_port_list(port_list); res = is_err; return res; } } if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG) { ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip, (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), fst_proto, nat_port, in_chain, exp_ts, now, "local NAT", spadat->spa_message_remain); } else if(strlen(fwd_chain->to_chain)) { /* FORWARD access rule */ forward_access_rule(opts, acc, fwd_chain, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); } /* DNAT rule */ if(strlen(dnat_chain->to_chain) && !acc->disable_dnat) dnat_rule(opts, acc, dnat_chain, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); if(acc->force_snat) snat_rule(opts, acc, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); } else if(acc->force_nat) /* handle force NAT scenarios */ { strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip)); nat_port = acc->force_nat_port; /* FORWARD access rule */ if(strlen(fwd_chain->to_chain)) forward_access_rule(opts, acc, fwd_chain, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); /* DNAT rule */ if(strlen(dnat_chain->to_chain) && !acc->disable_dnat) dnat_rule(opts, acc, dnat_chain, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); /* SNAT rule if required - we only allow this for FORCE_NAT * access stanzas for now until a new SPA packet type is added. */ if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0) snat_rule(opts, acc, nat_ip, nat_port, fst_proto, fst_port, spadat, exp_ts, now); } else /* Non-NAT request - this is the typical case. */ { /* Create an access command for each proto/port for the source ip. */ while(ple != NULL) { ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip, (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), ple->proto, ple->port, in_chain, exp_ts, now, "access", spadat->spa_message_remain); /* We need to make a corresponding OUTPUT rule if out_chain target * is not NULL. */ if(strlen(out_chain->to_chain)) { ipt_rule(opts, NULL, IPT_OUT_RULE_ARGS, spadat->use_src_ip, (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP), ple->proto, ple->port, out_chain, exp_ts, now, "OUTPUT", spadat->spa_message_remain); } ple = ple->next; } } /* Done with the port list for access rules. */ free_acc_port_list(port_list); return(res); }
/* Process the SPA packet data */ void incoming_spa(fko_srv_options_t *opts) { /* Always a good idea to initialize ctx to null if it will be used * repeatedly (especially when using fko_new_with_data()). */ fko_ctx_t ctx = NULL; 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, pkt_data_len=0; int is_err, cmd_exec_success = 0, attempted_decrypt = 0; int conf_pkt_age = 0; char dump_buf[CTX_DUMP_BUFSIZE]; spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); /* This will hold our pertinent SPA data. */ spa_data_t spadat; /* Loop through all access stanzas looking for a match */ acc_stanza_t *acc = opts->acc_stanzas; acc_string_list_t *gpg_id_ndx; unsigned char is_gpg_match = 0; inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), spadat.pkt_source_ip, sizeof(spadat.pkt_source_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 * 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) { log_msg(LOG_DEBUG, "[%s] preprocess_spa_data() returned error %i: '%s' for incoming packet.", spadat.pkt_source_ip, res, get_errstr(res)); return; } if(opts->foreground == 1 && opts->verbose > 2) { printf("[+] candidate SPA packet payload:\n"); hex_dump(spa_pkt->packet_data, pkt_data_len); } if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { conf_pkt_age = strtol_wrapper(opts->config[CONF_MAX_SPA_PACKET_AGE], 0, RCHK_MAX_SPA_PACKET_AGE, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] [%s] invalid MAX_SPA_PACKET_AGE", spadat.pkt_source_ip); return; } } if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip))) { if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { /* Check for a replay attack */ res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data); if(res != FKO_SUCCESS) { if (raw_digest != NULL) free(raw_digest); return; } if (raw_digest == NULL) return; if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS) { free(raw_digest); return; } } } else { log_msg(LOG_WARNING, "No access data found for source IP: %s", spadat.pkt_source_ip ); return; } /* Now that we know there is a matching access.conf stanza and the * incoming SPA packet is not a replay, see if we should grant any * access */ while(acc) { res = FKO_SUCCESS; cmd_exec_success = 0; attempted_decrypt = 0; stanza_num++; /* Start access loop with a clean FKO context */ if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); ctx = NULL; } /* Check for a match for the SPA source IP and the access stanza */ if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip))) { acc = acc->next; continue; } log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match", stanza_num, spadat.pkt_source_ip); log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data); /* Make sure this access stanza has not expired */ if(acc->access_expire_time > 0) { if(acc->expired) { acc = acc->next; continue; } else { if(time(NULL) > acc->access_expire_time) { log_msg(LOG_INFO, "[%s] (stanza #%d) Access stanza has expired", spadat.pkt_source_ip, stanza_num); acc->expired = 1; acc = acc->next; continue; } } } /* Get encryption type and try its decoding routine first (if the key * for that type is set) */ enc_type = fko_encryption_type((char *)spa_pkt->packet_data); if(acc->use_rijndael) { if (acc->key == NULL) { log_msg(LOG_ERR, "[%s] (stanza #%d) No KEY for RIJNDAEL encrypted messages", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } /* Command mode messages may be quite long */ if(acc->enable_cmd_exec || enc_type == FKO_ENCRYPTION_RIJNDAEL) { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key, acc->key_len, acc->encryption_mode, acc->hmac_key, acc->hmac_key_len, acc->hmac_type); attempted_decrypt = 1; if(res == FKO_SUCCESS) cmd_exec_success = 1; } } if(acc->use_gpg && enc_type == FKO_ENCRYPTION_GPG && cmd_exec_success == 0) { /* For GPG we create the new context without decrypting on the fly * so we can set some GPG parameters first. */ if(acc->gpg_decrypt_pw != NULL || acc->gpg_allow_no_pw) { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL, 0, FKO_ENC_MODE_ASYMMETRIC, acc->hmac_key, acc->hmac_key_len, acc->hmac_type); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error creating fko context (before decryption): %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); acc = acc->next; continue; } /* Set whatever GPG parameters we have. */ if(acc->gpg_exe != NULL) { res = fko_set_gpg_exe(ctx, acc->gpg_exe); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error setting GPG path %s: %s", spadat.pkt_source_ip, stanza_num, acc->gpg_exe, fko_errstr(res) ); acc = acc->next; continue; } } if(acc->gpg_home_dir != NULL) { res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error setting GPG keyring path to %s: %s", spadat.pkt_source_ip, stanza_num, acc->gpg_home_dir, fko_errstr(res) ); acc = acc->next; continue; } } if(acc->gpg_decrypt_id != NULL) fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id); /* If GPG_REQUIRE_SIG is set for this acc stanza, then set * the FKO context accordingly and check the other GPG Sig- * related parameters. This also applies when REMOTE_ID is * set. */ if(acc->gpg_require_sig) { fko_set_gpg_signature_verify(ctx, 1); /* Set whether or not to ignore signature verification errors. */ fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error); } else { fko_set_gpg_signature_verify(ctx, 0); fko_set_gpg_ignore_verify_error(ctx, 1); } /* Now decrypt the data. */ res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw, 0); attempted_decrypt = 1; } } if(attempted_decrypt == 0) { log_msg(LOG_ERR, "(stanza #%d) No stanza encryption mode match for encryption type: %i.", stanza_num, enc_type); acc = acc->next; continue; } /* Do we have a valid FKO context? Did the SPA decrypt properly? */ if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error creating fko context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); if(IS_GPG_ERROR(res)) log_msg(LOG_WARNING, "[%s] (stanza #%d) - GPG ERROR: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } /* Add this SPA packet into the replay detection cache */ if (added_replay_digest == 0 && strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { res = add_replay(opts, raw_digest); if (res != SPA_MSG_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Could not add digest to replay cache", spadat.pkt_source_ip, stanza_num); acc = acc->next; continue; } added_replay_digest = 1; } /* At this point, we assume the SPA data is valid. Now we need to see * if it meets our access criteria. */ log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):", spadat.pkt_source_ip, stanza_num, res); res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf)); if (res == FKO_SUCCESS) log_msg(LOG_DEBUG, "%s", dump_buf); else log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res)); /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty, * then we need to make sure this incoming message is signer ID matches * an entry in the list. */ if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig) { res = fko_get_gpg_signature_id(ctx, &gpg_id); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error pulling the GPG signature ID from the context: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } log_msg(LOG_INFO, "[%s] (stanza #%d) Incoming SPA data signed by '%s'.", spadat.pkt_source_ip, stanza_num, gpg_id); if(acc->gpg_remote_id != NULL) { is_gpg_match = 0; for(gpg_id_ndx = acc->gpg_remote_id_list; gpg_id_ndx != NULL; gpg_id_ndx=gpg_id_ndx->next) { res = fko_gpg_signature_id_match(ctx, gpg_id_ndx->str, &is_gpg_match); if(res != FKO_SUCCESS) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error in GPG siganture comparision: %s", spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx)); acc = acc->next; continue; } if(is_gpg_match) break; } if(! is_gpg_match) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.", spadat.pkt_source_ip, stanza_num, gpg_id); acc = acc->next; continue; } } } /* Populate our spa data struct for future reference. */ res = get_spa_data_fields(ctx, &spadat); /* Figure out what our timeout will be. If it is specified in the SPA * data, then use that. If not, try the FW_ACCESS_TIMEOUT from the * access.conf file (if there is one). Otherwise use the default. */ if(spadat.client_timeout > 0) spadat.fw_access_timeout = spadat.client_timeout; else if(acc->fw_access_timeout > 0) spadat.fw_access_timeout = acc->fw_access_timeout; else spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT; if(res != FKO_SUCCESS) { log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } /* Check packet age if so configured. */ if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0) { time(&now_ts); ts_diff = abs(now_ts - spadat.timestamp); if(ts_diff > conf_pkt_age) { log_msg(LOG_WARNING, "[%s] (stanza #%d) SPA data time difference is too great (%i seconds).", spadat.pkt_source_ip, stanza_num, ts_diff); acc = acc->next; continue; } } /* At this point, we have enough to check the embedded (or packet source) * IP address against the defined access rights. We start by splitting * the spa msg source IP from the remainder of the message. */ spa_ip_demark = strchr(spadat.spa_message, ','); if(spa_ip_demark == NULL) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); acc = acc->next; continue; } if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1 || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); ctx = NULL; } break; } strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1); if(! is_valid_ipv4_addr(spadat.spa_message_src_ip)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet", spadat.pkt_source_ip, stanza_num, fko_errstr(res)); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); ctx = NULL; } break; } strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN); /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it * is allowed. */ if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0) { if(acc->require_source_address) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Got 0.0.0.0 when valid source IP was required.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } spadat.use_src_ip = spadat.pkt_source_ip; } else spadat.use_src_ip = spadat.spa_message_src_ip; /* If REQUIRE_USERNAME is set, make sure the username in this SPA data * matches. */ if(acc->require_username != NULL) { if(strcmp(spadat.username, acc->require_username) != 0) { log_msg(LOG_WARNING, "[%s] (stanza #%d) Username in SPA data (%s) does not match required username: %s", spadat.pkt_source_ip, stanza_num, spadat.username, acc->require_username ); acc = acc->next; continue; } } /* Take action based on SPA message type. */ 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) { #if FIREWALL_IPTABLES if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0) { log_msg(LOG_WARNING, "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled", stanza_num, spadat.pkt_source_ip ); acc = acc->next; continue; } #else log_msg(LOG_WARNING, "(stanza #%d) SPA packet from %s requested unsupported NAT access", stanza_num, spadat.pkt_source_ip ); acc = acc->next; continue; #endif } /* Command messages. */ if(spadat.message_type == FKO_COMMAND_MSG) { if(!acc->enable_cmd_exec) { log_msg(LOG_WARNING, "[%s] (stanza #%d) SPA Command message are not allowed in the current configuration.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } else { log_msg(LOG_INFO, "[%s] (stanza #%d) Processing SPA Command message: command='%s'.", spadat.pkt_source_ip, stanza_num, spadat.spa_message_remain ); /* Do we need to become another user? If so, we call * run_extcmd_as and pass the cmd_exec_uid. */ if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0) { log_msg(LOG_INFO, "[%s] (stanza #%d) Setting effective user to %s (UID=%i) before running command.", spadat.pkt_source_ip, stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid); res = run_extcmd_as(acc->cmd_exec_uid, spadat.spa_message_remain, NULL, 0, 0); } else /* Just run it as we are (root that is). */ res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5); /* --DSS XXX: I have found that the status (and res for that * matter) have been unreliable indicators of the * actual exit status of some commands. Not sure * why yet. For now, we will take what we get. */ status = WEXITSTATUS(res); if(opts->verbose > 1) log_msg(LOG_WARNING, "[%s] (stanza #%d) CMD_EXEC: command returned %i", spadat.pkt_source_ip, stanza_num, status); if(status != 0) res = SPA_MSG_COMMAND_ERROR; if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num, fko_errstr(res) ); ctx = NULL; } /* we processed the command on a matching access stanza, so we * don't look for anything else to do with this SPA packet */ break; } } /* 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. */ if(! acc_check_port_access(acc, spadat.spa_message_remain)) { log_msg(LOG_WARNING, "[%s] (stanza #%d) One or more requested protocol/ports was denied per access.conf.", spadat.pkt_source_ip, stanza_num ); acc = acc->next; continue; } /* At this point, we process the SPA request and break out of the * access stanza loop (first valid access stanza stops us looking * for others). */ process_spa_request(opts, acc, &spadat); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip, stanza_num ); ctx = NULL; } break; } if (raw_digest != NULL) free(raw_digest); if(ctx != NULL) { if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA) log_msg(LOG_WARNING, "[%s] fko_destroy() could not zero out sensitive data buffer.", spadat.pkt_source_ip ); ctx = NULL; } return; }
static void snat_rule(const fko_srv_options_t * const opts, const acc_stanza_t * const acc, const char * const nat_ip, const unsigned int nat_port, const unsigned int fst_proto, const unsigned int fst_port, spa_data_t * const spadat, const unsigned int exp_ts, const time_t now) { char rule_buf[CMD_BUFSIZE] = {0}; char snat_target[SNAT_TARGET_BUFSIZE] = {0}; struct fw_chain *snat_chain = NULL; log_msg(LOG_DEBUG, "snat_rule() forward_all: %d, nat_ip: %s, nat_port: %d", acc->forward_all, nat_ip, nat_port); if(acc->forward_all) { /* Add SNAT or MASQUERADE rules. */ if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s", acc->force_snat_ip); } else if(acc->force_snat && acc->force_masquerade) { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, " "); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s", opts->config[CONF_SNAT_TRANSLATE_IP]); } else { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, " "); } memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_ALL_RULE_ARGS, snat_chain->table, spadat->use_src_ip, exp_ts, snat_chain->target, snat_target ); ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip, NULL, ANY_PROTO, ANY_PORT, snat_chain, exp_ts, now, "SNAT ALL", "*/*"); } else { /* Add SNAT or MASQUERADE rules. */ if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s:%i", acc->force_snat_ip, fst_port); } else if(acc->force_snat && acc->force_masquerade) { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-ports %i", fst_port); } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP], fst_port); } else { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-ports %i", fst_port); } memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS, snat_chain->table, fst_proto, nat_ip, nat_port, exp_ts, snat_chain->target, snat_target ); 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); } return; }
/* Set defaults, and do sanity and bounds checks for the various options. */ static void validate_options(fko_srv_options_t *opts) { char tmp_path[MAX_PATH_LEN] = {0}; /* If no conf dir is set in the config file, use the default. */ if(opts->config[CONF_FWKNOP_CONF_DIR] == NULL) set_config_entry(opts, CONF_FWKNOP_CONF_DIR, DEF_CONF_DIR); /* If no access.conf path was specified on the command line or set in * the config file, use the default. */ if(opts->config[CONF_ACCESS_FILE] == NULL) set_config_entry(opts, CONF_ACCESS_FILE, DEF_ACCESS_FILE); /* If the pid and digest cache files where not set in the config file or * via command-line, then grab the defaults. Start with RUN_DIR as the * files may depend on that. */ if(opts->config[CONF_FWKNOP_RUN_DIR] == NULL) set_config_entry(opts, CONF_FWKNOP_RUN_DIR, DEF_RUN_DIR); if(opts->config[CONF_FWKNOP_PID_FILE] == NULL) { strlcpy(tmp_path, opts->config[CONF_FWKNOP_RUN_DIR], sizeof(tmp_path)); if(tmp_path[strlen(tmp_path)-1] != '/') strlcat(tmp_path, "/", sizeof(tmp_path)); strlcat(tmp_path, DEF_PID_FILENAME, sizeof(tmp_path)); set_config_entry(opts, CONF_FWKNOP_PID_FILE, tmp_path); } #if USE_FILE_CACHE if(opts->config[CONF_DIGEST_FILE] == NULL) #else if(opts->config[CONF_DIGEST_DB_FILE] == NULL) #endif { strlcpy(tmp_path, opts->config[CONF_FWKNOP_RUN_DIR], sizeof(tmp_path)); if(tmp_path[strlen(tmp_path)-1] != '/') strlcat(tmp_path, "/", sizeof(tmp_path)); #if USE_FILE_CACHE strlcat(tmp_path, DEF_DIGEST_CACHE_FILENAME, sizeof(tmp_path)); set_config_entry(opts, CONF_DIGEST_FILE, tmp_path); #else strlcat(tmp_path, DEF_DIGEST_CACHE_DB_FILENAME, sizeof(tmp_path)); set_config_entry(opts, CONF_DIGEST_DB_FILE, tmp_path); #endif } /* Set remaining require CONF_ vars if they are not already set. */ /* PCAP capture interface - note that if '-r <pcap file>' 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); /* PCAP Promiscuous mode. */ if(opts->config[CONF_ENABLE_PCAP_PROMISC] == NULL) set_config_entry(opts, CONF_ENABLE_PCAP_PROMISC, DEF_ENABLE_PCAP_PROMISC); /* The packet count argument to pcap_dispatch() */ if(opts->config[CONF_PCAP_DISPATCH_COUNT] == NULL) set_config_entry(opts, CONF_PCAP_DISPATCH_COUNT, DEF_PCAP_DISPATCH_COUNT); /* Microseconds to sleep between pcap loop iterations */ if(opts->config[CONF_PCAP_LOOP_SLEEP] == NULL) set_config_entry(opts, CONF_PCAP_LOOP_SLEEP, DEF_PCAP_LOOP_SLEEP); /* PCAP Filter. */ if(opts->config[CONF_PCAP_FILTER] == NULL) set_config_entry(opts, CONF_PCAP_FILTER, DEF_PCAP_FILTER); /* Enable SPA packet aging unless we're getting packet data * directly from a pcap file */ if(opts->config[CONF_ENABLE_SPA_PACKET_AGING] == NULL) { 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. */ if(opts->config[CONF_MAX_SPA_PACKET_AGE] == NULL) set_config_entry(opts, CONF_MAX_SPA_PACKET_AGE, DEF_MAX_SPA_PACKET_AGE); /* Enable digest persistence. */ if(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE] == NULL) set_config_entry(opts, CONF_ENABLE_DIGEST_PERSISTENCE, DEF_ENABLE_DIGEST_PERSISTENCE); /* Max sniff bytes. */ if(opts->config[CONF_MAX_SNIFF_BYTES] == NULL) set_config_entry(opts, CONF_MAX_SNIFF_BYTES, DEF_MAX_SNIFF_BYTES); #if FIREWALL_IPTABLES /* Enable IPT forwarding. */ if(opts->config[CONF_ENABLE_IPT_FORWARDING] == NULL) set_config_entry(opts, CONF_ENABLE_IPT_FORWARDING, DEF_ENABLE_IPT_FORWARDING); /* Enable IPT local NAT. */ if(opts->config[CONF_ENABLE_IPT_LOCAL_NAT] == NULL) set_config_entry(opts, CONF_ENABLE_IPT_LOCAL_NAT, DEF_ENABLE_IPT_LOCAL_NAT); /* Enable IPT SNAT. */ if(opts->config[CONF_ENABLE_IPT_SNAT] == NULL) set_config_entry(opts, CONF_ENABLE_IPT_SNAT, DEF_ENABLE_IPT_SNAT); /* Make sure we have a valid IP if SNAT is enabled */ if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0) { /* Note that fw_config_init() will set use_masquerade if necessary */ if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) { if(! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { log_msg(LOG_ERR, "Invalid IPv4 addr for SNAT_TRANSLATE_IP" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } } } /* Enable IPT OUTPUT. */ if(opts->config[CONF_ENABLE_IPT_OUTPUT] == NULL) set_config_entry(opts, CONF_ENABLE_IPT_OUTPUT, DEF_ENABLE_IPT_OUTPUT); /* Flush IPT at init. */ if(opts->config[CONF_FLUSH_IPT_AT_INIT] == NULL) set_config_entry(opts, CONF_FLUSH_IPT_AT_INIT, DEF_FLUSH_IPT_AT_INIT); /* Flush IPT at exit. */ if(opts->config[CONF_FLUSH_IPT_AT_EXIT] == NULL) set_config_entry(opts, CONF_FLUSH_IPT_AT_EXIT, DEF_FLUSH_IPT_AT_EXIT); /* IPT input access. */ if(opts->config[CONF_IPT_INPUT_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_INPUT_ACCESS, DEF_IPT_INPUT_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_INPUT_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_INPUT_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* IPT output access. */ if(opts->config[CONF_IPT_OUTPUT_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_OUTPUT_ACCESS, DEF_IPT_OUTPUT_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_OUTPUT_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_OUTPUT_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* IPT forward access. */ if(opts->config[CONF_IPT_FORWARD_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_FORWARD_ACCESS, DEF_IPT_FORWARD_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_FORWARD_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_FORWARD_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* IPT dnat access. */ if(opts->config[CONF_IPT_DNAT_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_DNAT_ACCESS, DEF_IPT_DNAT_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_DNAT_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_DNAT_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* IPT snat access. */ if(opts->config[CONF_IPT_SNAT_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_SNAT_ACCESS, DEF_IPT_SNAT_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_SNAT_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_SNAT_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* IPT masquerade access. */ if(opts->config[CONF_IPT_MASQUERADE_ACCESS] == NULL) set_config_entry(opts, CONF_IPT_MASQUERADE_ACCESS, DEF_IPT_MASQUERADE_ACCESS); if(validate_ipt_chain_conf(opts->config[CONF_IPT_MASQUERADE_ACCESS]) != 1) { log_msg(LOG_ERR, "Invalid IPT_MASQUERADE_ACCESS specification, see fwknopd.conf comments" ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } /* Check for the iptables 'comment' match at init time */ if(opts->config[CONF_ENABLE_IPT_COMMENT_CHECK] == NULL) set_config_entry(opts, CONF_ENABLE_IPT_COMMENT_CHECK, DEF_ENABLE_IPT_COMMENT_CHECK); #elif FIREWALL_IPFW /* Flush ipfw rules at init. */ if(opts->config[CONF_FLUSH_IPFW_AT_INIT] == NULL) set_config_entry(opts, CONF_FLUSH_IPFW_AT_INIT, DEF_FLUSH_IPFW_AT_INIT); /* Flush ipfw rules at exit. */ if(opts->config[CONF_FLUSH_IPFW_AT_EXIT] == NULL) set_config_entry(opts, CONF_FLUSH_IPFW_AT_EXIT, DEF_FLUSH_IPFW_AT_EXIT); /* Set IPFW start rule number. */ if(opts->config[CONF_IPFW_START_RULE_NUM] == NULL) set_config_entry(opts, CONF_IPFW_START_RULE_NUM, DEF_IPFW_START_RULE_NUM); /* Set IPFW max rules. */ if(opts->config[CONF_IPFW_MAX_RULES] == NULL) set_config_entry(opts, CONF_IPFW_MAX_RULES, DEF_IPFW_MAX_RULES); /* Set IPFW active set number. */ if(opts->config[CONF_IPFW_ACTIVE_SET_NUM] == NULL) set_config_entry(opts, CONF_IPFW_ACTIVE_SET_NUM, DEF_IPFW_ACTIVE_SET_NUM); /* Set IPFW expire set number. */ if(opts->config[CONF_IPFW_EXPIRE_SET_NUM] == NULL) set_config_entry(opts, CONF_IPFW_EXPIRE_SET_NUM, DEF_IPFW_EXPIRE_SET_NUM); /* Set IPFW Dynamic rule expiry interval. */ if(opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL] == NULL) set_config_entry(opts, CONF_IPFW_EXPIRE_PURGE_INTERVAL, DEF_IPFW_EXPIRE_PURGE_INTERVAL); /* Set IPFW Dynamic rule expiry interval. */ if(opts->config[CONF_IPFW_ADD_CHECK_STATE] == NULL) set_config_entry(opts, CONF_IPFW_ADD_CHECK_STATE, DEF_IPFW_ADD_CHECK_STATE); #elif FIREWALL_PF /* Set PF anchor name */ if(opts->config[CONF_PF_ANCHOR_NAME] == NULL) set_config_entry(opts, CONF_PF_ANCHOR_NAME, DEF_PF_ANCHOR_NAME); /* Set PF rule expiry interval. */ if(opts->config[CONF_PF_EXPIRE_INTERVAL] == NULL) set_config_entry(opts, CONF_PF_EXPIRE_INTERVAL, DEF_PF_EXPIRE_INTERVAL); #elif FIREWALL_IPF /* --DSS Place-holder */ #endif /* FIREWALL type */ /* GPG Home dir. */ if(opts->config[CONF_GPG_HOME_DIR] == NULL) set_config_entry(opts, CONF_GPG_HOME_DIR, DEF_GPG_HOME_DIR); /* Enable SPA over HTTP. */ if(opts->config[CONF_ENABLE_SPA_OVER_HTTP] == NULL) set_config_entry(opts, CONF_ENABLE_SPA_OVER_HTTP, DEF_ENABLE_SPA_OVER_HTTP); /* Enable TCP server. */ if(opts->config[CONF_ENABLE_TCP_SERVER] == NULL) set_config_entry(opts, CONF_ENABLE_TCP_SERVER, DEF_ENABLE_TCP_SERVER); /* TCP Server port. */ if(opts->config[CONF_TCPSERV_PORT] == NULL) set_config_entry(opts, CONF_TCPSERV_PORT, DEF_TCPSERV_PORT); /* Syslog identity. */ if(opts->config[CONF_SYSLOG_IDENTITY] == NULL) set_config_entry(opts, CONF_SYSLOG_IDENTITY, DEF_SYSLOG_IDENTITY); /* Syslog facility. */ if(opts->config[CONF_SYSLOG_FACILITY] == NULL) set_config_entry(opts, CONF_SYSLOG_FACILITY, DEF_SYSLOG_FACILITY); /* Validate integer variable ranges */ validate_int_var_ranges(opts); /* Some options just trigger some output of information, or trigger an * external function, but do not actually start fwknopd. If any of those * are set, we can return here an skip the validation routines as all * other options will be ignored anyway. * * These are also mutually exclusive (for now). */ if((opts->dump_config + opts->kill + opts->restart + opts->status) == 1) return; if((opts->dump_config + opts->kill + opts->restart + opts->status) > 1) { log_msg(LOG_ERR, "The -D, -K, -R, and -S options are mutually exclusive. Pick only one." ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } if(opts->config[CONF_FIREWALL_EXE] == NULL) { log_msg(LOG_ERR, "[*] No firewall command executable is set. Please check FIREWALL_EXE in fwknopd.conf." ); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } return; }
/* Validate and in some cases preprocess/reformat the SPA data. Return an * error code value if there is any indication the data is not valid spa data. */ static int preprocess_spa_data(const fko_srv_options_t *opts, spa_pkt_info_t *spa_pkt, spa_data_t *spadat) { char *ndx = (char *)&(spa_pkt->packet_data); char *xff; int i, pkt_data_len = 0; pkt_data_len = spa_pkt->packet_data_len; /* At this point, we can reset the packet data length to 0. This is our * indicator to the rest of the program that we do not have a current * spa packet to process (after this one that is). */ spa_pkt->packet_data_len = 0; /* These two checks are already done in process_packet(), but this is a * defensive measure to run them again here */ if(pkt_data_len < MIN_SPA_DATA_SIZE) return(SPA_MSG_BAD_DATA); if(pkt_data_len > MAX_SPA_PACKET_LEN) return(SPA_MSG_BAD_DATA); /* Ignore any SPA packets that contain the Rijndael or GnuPG prefixes * since an attacker might have tacked them on to a previously seen * SPA packet in an attempt to get past the replay check. And, we're * no worse off since a legitimate SPA packet that happens to include * a prefix after the outer one is stripped off won't decrypt properly * anyway because libfko would not add a new one. */ if(constant_runtime_cmp(ndx, B64_RIJNDAEL_SALT, B64_RIJNDAEL_SALT_STR_LEN) == 0) return(SPA_MSG_BAD_DATA); if(pkt_data_len > MIN_GNUPG_MSG_SIZE && constant_runtime_cmp(ndx, B64_GPG_PREFIX, B64_GPG_PREFIX_STR_LEN) == 0) return(SPA_MSG_BAD_DATA); /* Initialize X-Forwarded-For field */ spadat->pkt_source_xff_ip[0] = '\0'; /* Detect and parse out SPA data from an HTTP request. If the SPA data * starts with "GET /" and the user agent starts with "Fwknop", then * assume it is a SPA over HTTP request. */ if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "Y", 1) == 0 && strncasecmp(ndx, "GET /", 5) == 0 && strstr(ndx, "User-Agent: Fwknop") != NULL) { /* This looks like an HTTP request, so let's see if we are * configured to accept such request and if so, find the SPA * data. */ /* Process X-Forwarded-For header */ xff = strcasestr(ndx, "X-Forwarded-For: "); if (xff != NULL) { xff += 17; for (i = 0; *xff != '\0'; i++) if (isspace(*xff)) *xff = '\0'; else xff++; xff -= i - 1; if (!is_valid_ipv4_addr(xff)) log_msg(LOG_WARNING, "Error parsing X-Forwarded-For header: value '%s' is not an IP address", xff); else strlcpy(spadat->pkt_source_xff_ip, xff, i); } /* Now extract, adjust (convert characters translated by the fwknop * client), and reset the SPA message itself. */ strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len); pkt_data_len -= 5; for(i=0; i<pkt_data_len; i++) { if(isspace(*ndx)) /* The first space marks the end of the req */ { *ndx = '\0'; break; } else if(*ndx == '-') /* Convert '-' to '+' */ *ndx = '+'; else if(*ndx == '_') /* Convert '_' to '/' */ *ndx = '/'; ndx++; } if(i < MIN_SPA_DATA_SIZE) return(SPA_MSG_BAD_DATA); spa_pkt->packet_data_len = pkt_data_len = i; } /* Require base64-encoded data */ if(! is_base64(spa_pkt->packet_data, pkt_data_len)) return(SPA_MSG_NOT_SPA_DATA); /* If we made it here, we have no reason to assume this is not SPA data. * The ultimate test will be whether the SPA data authenticates via an * HMAC anyway. */ return(FKO_SUCCESS); }
/* Rule Processing - Create an access request... */ int process_spa_request(const fko_srv_options_t * const opts, const acc_stanza_t * const acc, spa_data_t * const spadat) { char nat_ip[MAX_IPV4_STR_LEN] = {0}; char snat_target[SNAT_TARGET_BUFSIZE] = {0}; char rule_buf[CMD_BUFSIZE] = {0}; char *ndx; unsigned int nat_port = 0; acc_port_list_t *port_list = NULL; acc_port_list_t *ple = NULL; unsigned int fst_proto; unsigned int fst_port; struct fw_chain * const in_chain = &(opts->fw_config->chain[IPT_INPUT_ACCESS]); struct fw_chain * const out_chain = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]); struct fw_chain * const fwd_chain = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]); struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]); struct fw_chain *snat_chain; /* We assign this later (if we need to). */ int res = 0, is_err, snat_chain_num = 0; time_t now; unsigned int exp_ts; /* Parse and expand our access message. */ if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1) { /* technically we would already have exited with an error if there were * any memory allocation errors (see the add_port_list() function), but * for completeness... */ free_acc_port_list(port_list); return res; } /* Start at the top of the proto-port list... */ ple = port_list; /* Remember the first proto/port combo in case we need them * for NAT access requests. */ fst_proto = ple->proto; fst_port = ple->port; /* Set our expire time value. */ time(&now); exp_ts = now + spadat->fw_access_timeout; /* For straight access requests, we currently support multiple proto/port * request. */ if((spadat->message_type == FKO_ACCESS_MSG || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG) && !acc->force_nat) { /* Check to make sure that the jump rules exist for each * required chain */ if(chain_exists(opts, IPT_INPUT_ACCESS) == 0) create_chain(opts, IPT_INPUT_ACCESS); if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0) add_jump_rule(opts, IPT_INPUT_ACCESS); if(strlen(out_chain->to_chain)) { if(chain_exists(opts, IPT_OUTPUT_ACCESS) == 0) create_chain(opts, IPT_OUTPUT_ACCESS); if(jump_rule_exists(opts, IPT_OUTPUT_ACCESS) == 0) add_jump_rule(opts, IPT_OUTPUT_ACCESS); } /* Create an access command for each proto/port for the source ip. */ while(ple != NULL) { memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS, in_chain->table, ple->proto, spadat->use_src_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) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u", in_chain->to_chain, spadat->use_src_ip, spadat->spa_message_remain, exp_ts ); in_chain->active_rules++; /* Reset the next expected expire time for this chain if it * is warranted. */ if(in_chain->next_expire < now || exp_ts < in_chain->next_expire) in_chain->next_expire = exp_ts; } } /* If we have to make an corresponding OUTPUT rule if out_chain target * is not NULL. */ if(strlen(out_chain->to_chain)) { memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_OUT_RULE_ARGS, out_chain->table, ple->proto, spadat->use_src_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) { if(create_rule(opts, out_chain->to_chain, rule_buf)) { log_msg(LOG_INFO, "Added Rule in %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; } } } ple = ple->next; } } /* NAT requests... */ else 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 || acc->force_nat) { /* Parse out the NAT IP and Port components. */ if(acc->force_nat) { strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip)); nat_port = acc->force_nat_port; } else { ndx = strchr(spadat->nat_access, ','); if(ndx != NULL) { strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1); if (! is_valid_ipv4_addr(nat_ip)) { log_msg(LOG_INFO, "Invalid NAT IP in SPA message"); free_acc_port_list(port_list); return res; } 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"); free_acc_port_list(port_list); res = is_err; return res; } } } if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG) { memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS, in_chain->table, fst_proto, spadat->use_src_ip, nat_port, exp_ts, in_chain->target ); /* Check to make sure that the jump rules exist for each * required chain */ if(chain_exists(opts, IPT_INPUT_ACCESS) == 0) create_chain(opts, IPT_INPUT_ACCESS); if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0) 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) { if(create_rule(opts, in_chain->to_chain, rule_buf)) { log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u", in_chain->to_chain, spadat->use_src_ip, spadat->spa_message_remain, exp_ts ); in_chain->active_rules++; /* Reset the next expected expire time for this chain if it * is warranted. */ if(in_chain->next_expire < now || exp_ts < in_chain->next_expire) in_chain->next_expire = exp_ts; } } } else if(strlen(fwd_chain->to_chain)) { /* Make our FORWARD and NAT rules, and make sure the * required chain and jump rule exists */ if(chain_exists(opts, IPT_FORWARD_ACCESS) == 0) create_chain(opts, IPT_FORWARD_ACCESS); if (jump_rule_exists(opts, IPT_FORWARD_ACCESS) == 0) add_jump_rule(opts, IPT_FORWARD_ACCESS); memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_FWD_RULE_ARGS, fwd_chain->table, fst_proto, spadat->use_src_ip, nat_ip, nat_port, exp_ts, fwd_chain->target ); if(rule_exists(opts, fwd_chain, rule_buf, fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, fwd_chain->to_chain, rule_buf)) { 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; } } } if(strlen(dnat_chain->to_chain)) { /* Make sure the required chain and jump rule exist */ if(chain_exists(opts, IPT_DNAT_ACCESS) == 0) create_chain(opts, IPT_DNAT_ACCESS); if (jump_rule_exists(opts, IPT_DNAT_ACCESS) == 0) add_jump_rule(opts, IPT_DNAT_ACCESS); memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_DNAT_RULE_ARGS, dnat_chain->table, fst_proto, spadat->use_src_ip, fst_port, exp_ts, dnat_chain->target, nat_ip, nat_port ); if(rule_exists(opts, dnat_chain, rule_buf, fst_proto, spadat->use_src_ip, fst_port, exp_ts) == 0) { if(create_rule(opts, dnat_chain->to_chain, rule_buf)) { 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; } } } /* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well. */ if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0) { /* Add SNAT or MASQUERADE rules. */ if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip)) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s:%i", acc->force_snat_ip, fst_port); snat_chain_num = IPT_SNAT_ACCESS; } else if(acc->force_snat && acc->force_masquerade) { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-ports %i", fst_port); snat_chain_num = IPT_MASQUERADE_ACCESS; } else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL) && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { /* Using static SNAT */ snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP], fst_port); snat_chain_num = IPT_SNAT_ACCESS; } else { /* Using MASQUERADE */ snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]); snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, "--to-ports %i", fst_port); snat_chain_num = IPT_MASQUERADE_ACCESS; } if(chain_exists(opts, snat_chain_num) == 0) create_chain(opts, snat_chain_num); if(jump_rule_exists(opts, snat_chain_num) == 0) add_jump_rule(opts, snat_chain_num); memset(rule_buf, 0, CMD_BUFSIZE); snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS, snat_chain->table, fst_proto, nat_ip, nat_port, exp_ts, snat_chain->target, snat_target ); if(rule_exists(opts, snat_chain, rule_buf, fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0) { if(create_rule(opts, snat_chain->to_chain, rule_buf)) { log_msg(LOG_INFO, "Added SNAT Rule to %s for %s, %s expires at %u", snat_chain->to_chain, spadat->use_src_ip, spadat->spa_message_remain, exp_ts ); snat_chain->active_rules++; /* Reset the next expected expire time for this chain if it * is warranted. */ if(snat_chain->next_expire < now || exp_ts < snat_chain->next_expire) snat_chain->next_expire = exp_ts; } } } } /* Done with the port list for access rules. */ free_acc_port_list(port_list); return(res); }
int fw_config_init(fko_srv_options_t * const opts) { memset(&fwc, 0x0, sizeof(struct fw_config)); /* Set our firewall exe command path (iptables in most cases). */ strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command)); /* Pull the fwknop chain config info and setup our internal * config struct. The IPT_INPUT is the only one that is * required. The rest are optional. */ if(set_fw_chain_conf(IPT_INPUT_ACCESS, opts->config[CONF_IPT_INPUT_ACCESS]) != 1) return 0; /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_IPT_OUTPUT_ACCESS be Y */ if(strncasecmp(opts->config[CONF_ENABLE_IPT_OUTPUT], "Y", 1)==0) if(set_fw_chain_conf(IPT_OUTPUT_ACCESS, opts->config[CONF_IPT_OUTPUT_ACCESS]) != 1) return 0; /* The remaining access chains require ENABLE_IPT_FORWARDING = Y */ if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)==0) { if(set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]) != 1) return 0; if(set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]) != 1) return 0; /* SNAT (whichever mode) requires ENABLE_IPT_SNAT = Y */ if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1)==0) { if(opts->config[CONF_SNAT_TRANSLATE_IP] == NULL || ! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { fwc.use_masquerade = 1; if(set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]) != 1) return 0; } else { if(is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP])) { if(set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]) != 1) return 0; } else { return 0; } } } } /* Let us find it via our opts struct as well. */ opts->fw_config = &fwc; return 1; }