static void validate_int_var_ranges(fko_srv_options_t *opts) { #if FIREWALL_IPFW int is_err = FKO_SUCCESS; #endif range_check(opts, "PCAP_LOOP_SLEEP", opts->config[CONF_PCAP_LOOP_SLEEP], 1, RCHK_MAX_PCAP_LOOP_SLEEP); range_check(opts, "MAX_SPA_PACKET_AGE", opts->config[CONF_MAX_SPA_PACKET_AGE], 1, RCHK_MAX_SPA_PACKET_AGE); range_check(opts, "MAX_SNIFF_BYTES", opts->config[CONF_MAX_SNIFF_BYTES], 1, RCHK_MAX_SNIFF_BYTES); range_check(opts, "TCPSERV_PORT", opts->config[CONF_TCPSERV_PORT], 1, RCHK_MAX_TCPSERV_PORT); #if FIREWALL_IPFW range_check(opts, "IPFW_START_RULE_NUM", opts->config[CONF_IPFW_START_RULE_NUM], 0, RCHK_MAX_IPFW_START_RULE_NUM); range_check(opts, "IPFW_MAX_RULES", opts->config[CONF_IPFW_MAX_RULES], 1, RCHK_MAX_IPFW_MAX_RULES); range_check(opts, "IPFW_ACTIVE_SET_NUM", opts->config[CONF_IPFW_ACTIVE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM); range_check(opts, "IPFW_EXPIRE_SET_NUM", opts->config[CONF_IPFW_EXPIRE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM); range_check(opts, "IPFW_EXPIRE_PURGE_INTERVAL", opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL], 1, RCHK_MAX_IPFW_PURGE_INTERVAL); /* Make sure the active and expire sets are not identical whenever * they are non-zero */ if((strtol_wrapper(opts->config[CONF_IPFW_ACTIVE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err) > 0 && strtol_wrapper(opts->config[CONF_IPFW_EXPIRE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err) > 0) && strtol_wrapper(opts->config[CONF_IPFW_ACTIVE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err) == strtol_wrapper(opts->config[CONF_IPFW_EXPIRE_SET_NUM], 0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err)) { log_msg(LOG_ERR, "[*] Cannot set identical ipfw active and expire sets."); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid integer conversion error.\n"); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } #elif FIREWALL_PF range_check(opts, "PF_EXPIRE_INTERVAL", opts->config[CONF_PF_EXPIRE_INTERVAL], 1, RCHK_MAX_PF_EXPIRE_INTERVAL); #endif /* FIREWALL type */ return; }
static int parse_client_timeout(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx) { int is_err; if( ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) { if((*t_size = strlen(*ndx)) < 1) return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING); if (*t_size > MAX_SPA_MESSAGE_SIZE) return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG); /* Should be a number only. */ if(strspn(*ndx, "0123456789") != *t_size) return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL); ctx->client_timeout = (unsigned int) strtol_wrapper(*ndx, 0, (2 << 15), NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL); } return FKO_SUCCESS; }
static int parse_msg_type(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx) { int is_err, remaining_fields; if((*t_size = strcspn(*ndx, ":")) < 1) return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING); if(*t_size > MAX_SPA_MESSAGE_TYPE_SIZE) return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG); strlcpy(tbuf, *ndx, *t_size+1); ctx->message_type = strtol_wrapper(tbuf, 0, FKO_LAST_MSG_TYPE-1, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL); /* Now that we have a valid type, ensure that the total * number of SPA fields is also valid for the type */ remaining_fields = num_fields(*ndx); switch(ctx->message_type) { /* optional server_auth + digest */ case FKO_COMMAND_MSG: case FKO_ACCESS_MSG: if(remaining_fields > 2) return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS; break; /* nat or client timeout + optional server_auth + digest */ case FKO_NAT_ACCESS_MSG: case FKO_LOCAL_NAT_ACCESS_MSG: case FKO_CLIENT_TIMEOUT_ACCESS_MSG: if(remaining_fields > 3) return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS; break; /* client timeout + nat + optional server_auth + digest */ case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG: case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG: if(remaining_fields > 4) return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS; break; default: /* Should not reach here */ return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL); } *ndx += *t_size + 1; return FKO_SUCCESS; }
/* Check to see if an integer variable has a value that is within a * specific range */ static void range_check(fko_srv_options_t *opts, char *var, char *val, int low, int high) { int is_err; strtol_wrapper(val, low, high, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] var %s value '%s' not in the range %d-%d", var, val, low, high); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } return; }
static int get_rand_port(fko_ctx_t ctx) { char *rand_val = NULL; char port_str[MAX_PORT_STR_LEN+1] = {0}; int tmpint, is_err; int port = 0; int res = 0; res = fko_get_rand_value(ctx, &rand_val); if(res != FKO_SUCCESS) { errmsg("get_rand_port(), fko_get_rand_value", res); return -1; } strlcpy(port_str, rand_val, sizeof(port_str)); tmpint = strtol_wrapper(port_str, 0, -1, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_VERBOSITY_ERROR, "[*] get_rand_port(), could not convert rand_val str '%s', to integer", rand_val); return -1; } /* Convert to a random value between 1024 and 65535 */ port = (MIN_HIGH_PORT + (tmpint % (MAX_PORT - MIN_HIGH_PORT))); /* Force libfko to calculate a new random value since we don't want to * give anyone a hint (via the port value) about the contents of the * encrypted SPA data. */ res = fko_set_rand_value(ctx, NULL); if(res != FKO_SUCCESS) { errmsg("get_rand_port(), fko_get_rand_value", res); return -1; } return port; }
static pid_t get_running_pid(const fko_srv_options_t *opts) { int op_fd, is_err, bytes_read = 0; char buf[PID_BUFLEN] = {0}; pid_t rpid = 0; if(verify_file_perms_ownership(opts->config[CONF_FWKNOP_PID_FILE]) != 1) { fprintf(stderr, "verify_file_perms_ownership() error\n"); return(rpid); } op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY); if(op_fd == -1) { if((opts->foreground != 0) && (opts->verbose != 0)) perror("Error trying to open PID file: "); return(rpid); } bytes_read = read(op_fd, buf, PID_BUFLEN); if (bytes_read > 0) { buf[PID_BUFLEN-1] = '\0'; /* max pid value is configurable on Linux */ rpid = (pid_t) strtol_wrapper(buf, 0, (2 << 30), NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) rpid = 0; } else if (bytes_read < 0) perror("Error trying to read() PID file: "); close(op_fd); return(rpid); }
static int parse_timestamp(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx) { int is_err; if((*t_size = strcspn(*ndx, ":")) < 1) return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING); if (*t_size > MAX_SPA_TIMESTAMP_SIZE) return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG); strlcpy(tbuf, *ndx, *t_size+1); ctx->timestamp = (unsigned int) strtol_wrapper(tbuf, 0, -1, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL); *ndx += *t_size + 1; 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}; 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); }
int main(int argc, char **argv) { int res, last_sig, rp_cache_count, is_err; char *locale; pid_t old_pid; fko_srv_options_t opts; while(1) { /* Handle command line */ config_init(&opts, argc, argv); /* Process any options that do their thing and exit. */ /* Kill the currently running fwknopd? */ if(opts.kill == 1) { old_pid = get_running_pid(&opts); if(old_pid > 0) { res = kill(old_pid, SIGTERM); is_err = kill(old_pid, 0); if(res == 0 && is_err != 0) { fprintf(stdout, "Killed fwknopd (pid=%i)\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { /* give a bit of time for process shutdown and check again */ sleep(1); is_err = kill(old_pid, 0); if(is_err != 0) { fprintf(stdout, "Killed fwknopd (pid=%i) via SIGTERM\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { res = kill(old_pid, SIGKILL); is_err = kill(old_pid, 0); if(res == 0 && is_err != 0) { fprintf(stdout, "Killed fwknopd (pid=%i) via SIGKILL\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { sleep(1); is_err = kill(old_pid, 0); if(is_err != 0) { fprintf(stdout, "Killed fwknopd (pid=%i) via SIGKILL\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { perror("Unable to kill fwknop: "); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } } } } } else { fprintf(stderr, "No running fwknopd detected.\n"); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } } /* Status of the currently running fwknopd? */ if(opts.status == 1) { old_pid = write_pid_file(&opts); if(old_pid > 0) { fprintf(stdout, "Detected fwknopd is running (pid=%i).\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { fprintf(stdout, "No running fwknopd detected.\n"); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } } /* Restart the currently running fwknopd? */ if(opts.restart == 1 || opts.status == 1) { old_pid = get_running_pid(&opts); if(old_pid > 0) { res = kill(old_pid, SIGHUP); if(res == 0) { fprintf(stdout, "Sent restart signal to fwknopd (pid=%i)\n", old_pid); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } else { perror("Unable to send signal to fwknop: "); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } } else { fprintf(stdout, "No running fwknopd detected.\n"); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } } /* Initialize logging. */ init_logging(&opts); /* Update the verbosity level for the log module */ log_set_verbosity(LOG_DEFAULT_VERBOSITY + opts.verbose); #if HAVE_LOCALE_H /* Set the locale if specified. */ if(opts.config[CONF_LOCALE] != NULL && strncasecmp(opts.config[CONF_LOCALE], "NONE", 4) != 0) { locale = setlocale(LC_ALL, opts.config[CONF_LOCALE]); if(locale == NULL) { log_msg(LOG_ERR, "WARNING: Unable to set locale to '%s'.", opts.config[CONF_LOCALE] ); } else { log_msg(LOG_INFO, "Locale set to '%s'.", opts.config[CONF_LOCALE] ); } } #endif /* Make sure we have a valid run dir and path leading to digest file * in case it configured to be somewhere other than the run dir. */ check_dir_path((const char *)opts.config[CONF_FWKNOP_RUN_DIR], "Run", 0); /* Initialize the firewall rules handler based on the fwknopd.conf * file, but (for iptables firewalls) don't flush any rules or create * any chains yet. This allows us to dump the current firewall rules * via fw_rules_dump() in --fw-list mode before changing around any rules * of an existing fwknopd process. */ if(fw_config_init(&opts) != 1) clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); if(opts.fw_list == 1 || opts.fw_list_all == 1) { fw_dump_rules(&opts); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } if(opts.fw_flush == 1) { fprintf(stdout, "Deleting any existing firewall rules...\n"); clean_exit(&opts, FW_CLEANUP, EXIT_SUCCESS); } /* Process the access.conf file. */ parse_access_file(&opts); /* Show config (including access.conf vars) and exit dump config was * wanted. */ if(opts.dump_config == 1) { dump_config(&opts); dump_access_list(&opts); clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS); } /* If we are a new process (just being started), proceed with normal * start-up. Otherwise, we are here as a result of a signal sent to an * existing process and we want to restart. */ if(get_running_pid(&opts) != getpid()) { /* If foreground mode is not set, then fork off and become a daemon. * Otherwise, attempt to get the pid file lock and go on. */ if(opts.foreground == 0) { daemonize_process(&opts); } else { old_pid = write_pid_file(&opts); if(old_pid > 0) { fprintf(stderr, "[*] An instance of fwknopd is already running: (PID=%i).\n", old_pid ); clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE); } else if(old_pid < 0) { fprintf(stderr, "[*] PID file error. The lock may not be effective.\n"); } } log_msg(LOG_INFO, "Starting %s", MY_NAME); } else { log_msg(LOG_INFO, "Re-starting %s", MY_NAME); } if(opts.verbose > 1 && opts.foreground) { dump_config(&opts); dump_access_list(&opts); } /* Initialize the digest cache for replay attack detection (either * with dbm support or with the default simple cache file strategy) * if so configured. */ if(strncasecmp(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { rp_cache_count = replay_cache_init(&opts); if(rp_cache_count < 0) { log_msg(LOG_WARNING, "Error opening digest cache file. Incoming digests will not be remembered." ); /* Destination points to heap memory, and is guaranteed to be * at least two bytes large via validate_options(), * DEF_ENABLE_DIGEST_PERSISTENCE, and set_config_entry() */ strlcpy(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "N", 2); } if(opts.verbose) log_msg(LOG_ERR, "Using Digest Cache: '%s' (entry count = %i)", #if USE_FILE_CACHE opts.config[CONF_DIGEST_FILE], rp_cache_count #else opts.config[CONF_DIGEST_DB_FILE], rp_cache_count #endif ); } /* Prepare the firewall - i.e. flush any old rules and (for iptables) * create fwknop chains. */ if(fw_initialize(&opts) != 1) clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); /* If the TCP server option was set, fire it up here. */ if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0) { strtol_wrapper(opts.config[CONF_TCPSERV_PORT], 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err == FKO_SUCCESS) { run_tcp_server(&opts); } else { log_msg(LOG_WARNING, "WARNING: ENABLE_TCP_SERVER is set, but TCPSERV_PORT is not valid. TCP server not started!" ); } } /* Intiate pcap capture mode... */ pcap_capture(&opts); if(got_signal) { last_sig = got_signal; got_signal = 0; if(got_sighup) { log_msg(LOG_WARNING, "Got SIGHUP. Re-reading configs."); free_configs(&opts); kill(opts.tcp_server_pid, SIGTERM); usleep(1000000); got_sighup = 0; } else if(got_sigint) { log_msg(LOG_WARNING, "Got SIGINT. Exiting..."); got_sigint = 0; break; } else if(got_sigterm) { log_msg(LOG_WARNING, "Got SIGTERM. Exiting..."); got_sigterm = 0; break; } else { log_msg(LOG_WARNING, "Got signal %i. No defined action but to exit.", last_sig); break; } } else if (opts.packet_ctr_limit > 0 && opts.packet_ctr >= opts.packet_ctr_limit) { log_msg(LOG_INFO, "Packet count limit (%d) reached. Exiting...", opts.packet_ctr_limit); break; } else /* got_signal was not set (should be if we are here) */ { log_msg(LOG_WARNING, "Capture ended without signal. Exiting..."); break; } } log_msg(LOG_INFO, "Shutting Down fwknopd."); /* Kill the TCP server (if we have one running). */ if(opts.tcp_server_pid > 0) { log_msg(LOG_INFO, "Killing the TCP server (pid=%i)", opts.tcp_server_pid); kill(opts.tcp_server_pid, SIGTERM); /* --DSS XXX: This seems to be necessary if the tcp server * was restarted by this program. We need to * investigate and fix this. For now, this works * (it is kludgy, but does no harm afaik). */ kill(opts.tcp_server_pid, SIGKILL); } /* Other cleanup. */ fw_cleanup(&opts); free_logging(); #if USE_FILE_CACHE free_replay_list(&opts); #endif free_configs(&opts); return(0); }
/* Set NAT access string */ static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const access_buf) { char nat_access_buf[MAX_LINE_LEN] = {0}; char tmp_access_port[MAX_PORT_STR_LEN+1] = {0}, *ndx = NULL; int access_port = 0, i = 0, is_err = 0; char dst_ip_str[INET_ADDRSTRLEN] = {0}; char hostname[HOSTNAME_BUFSIZE] = {0}; int port = 0; struct addrinfo hints; memset(&hints, 0 , sizeof(hints)); ndx = strchr(options->access_str, '/'); if(ndx == NULL) { log_msg(LOG_VERBOSITY_ERROR, "[*] Expecting <proto>/<port> for -A arg."); return FKO_ERROR_INVALID_DATA; } ndx++; while(*ndx != '\0' && isdigit(*ndx) && i < MAX_PORT_STR_LEN) { tmp_access_port[i] = *ndx; ndx++; i++; } tmp_access_port[i] = '\0'; access_port = strtol_wrapper(tmp_access_port, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_VERBOSITY_ERROR, "[*] Invalid port value '%d' for -A arg.", access_port); return FKO_ERROR_INVALID_DATA; } if (options->nat_local && options->nat_access_str[0] == 0x0) { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->spa_server_str, access_port); } if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0) { if (ipv4_str_has_port(options->nat_access_str)) { snprintf(nat_access_buf, MAX_LINE_LEN, "%s", options->nat_access_str); } else { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->nat_access_str, access_port); } } /* Check if there is a hostname to resolve as an ip address in the NAT access buffer */ if (is_hostname_str_with_port(nat_access_buf, hostname, sizeof(hostname), &port)) { /* Speed up the name resolution by forcing ipv4 (AF_INET). * A NULL pointer could be used instead if there is no constraint. * Maybe when ipv6 support will be enable the structure could initialize the * family to either AF_INET or AF_INET6 */ hints.ai_family = AF_INET; if (resolve_dst_addr(hostname, &hints, dst_ip_str, sizeof(dst_ip_str), options) != 0) { log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an ip address", hostname); return FKO_ERROR_INVALID_DATA; } snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, dst_ip_str, port); } /* Nothing to resolve */ else; if(options->nat_rand_port) { /* Must print to stdout what the random port is since * if not then the user will not which port will be * opened/NAT'd on the fwknopd side */ log_msg(LOG_VERBOSITY_NORMAL, "[+] Randomly assigned port '%d' on: '%s' will grant access to: '%s'", options->nat_port, access_buf, nat_access_buf); } return fko_set_spa_nat_access(ctx, nat_access_buf); }
/* The pcap capture routine. */ int pcap_capture(fko_srv_options_t *opts) { pcap_t *pcap; char errstr[PCAP_ERRBUF_SIZE] = {0}; struct bpf_program fp; int res; int pcap_errcnt = 0; int pending_break = 0; int promisc = 0; int set_direction = 1; int pcap_file_mode = 0; int status; int useconds; int pcap_dispatch_count; int max_sniff_bytes; int is_err; pid_t child_pid; #if FIREWALL_IPFW time_t now; #endif useconds = strtol_wrapper(opts->config[CONF_PCAP_LOOP_SLEEP], 0, RCHK_MAX_PCAP_LOOP_SLEEP, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid PCAP_LOOP_SLEEP_value"); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } max_sniff_bytes = strtol_wrapper(opts->config[CONF_MAX_SNIFF_BYTES], 0, RCHK_MAX_SNIFF_BYTES, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid MAX_SNIFF_BYTES"); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } /* Set promiscuous mode if ENABLE_PCAP_PROMISC is set to 'Y'. */ if(strncasecmp(opts->config[CONF_ENABLE_PCAP_PROMISC], "Y", 1) == 0) promisc = 1; if(opts->config[CONF_PCAP_FILE] != NULL && opts->config[CONF_PCAP_FILE][0] != '\0') pcap_file_mode = 1; if(pcap_file_mode == 1) { log_msg(LOG_INFO, "Reading pcap file: %s", opts->config[CONF_PCAP_FILE]); pcap = pcap_open_offline(opts->config[CONF_PCAP_FILE], errstr); if(pcap == NULL) { log_msg(LOG_ERR, "[*] pcap_open_offline() error: %s", errstr); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } } else { log_msg(LOG_INFO, "Sniffing interface: %s", opts->config[CONF_PCAP_INTF]); pcap = pcap_open_live(opts->config[CONF_PCAP_INTF], max_sniff_bytes, promisc, 100, errstr ); if(pcap == NULL) { log_msg(LOG_ERR, "[*] pcap_open_live() error: %s", errstr); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } } /* Set pcap filters, if any. */ if (opts->config[CONF_PCAP_FILTER][0] != '\0') { if(pcap_compile(pcap, &fp, opts->config[CONF_PCAP_FILTER], 1, 0) == -1) { log_msg(LOG_ERR, "[*] Error compiling pcap filter: %s", pcap_geterr(pcap) ); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } if(pcap_setfilter(pcap, &fp) == -1) { log_msg(LOG_ERR, "[*] Error setting pcap filter: %s", pcap_geterr(pcap) ); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } log_msg(LOG_INFO, "PCAP filter is: '%s'", opts->config[CONF_PCAP_FILTER]); pcap_freecode(&fp); } /* Determine and set the data link encapsulation offset. */ switch(pcap_datalink(pcap)) { case DLT_EN10MB: opts->data_link_offset = 14; break; #if defined(__linux__) case DLT_LINUX_SLL: opts->data_link_offset = 16; break; #elif defined(__OpenBSD__) case DLT_LOOP: set_direction = 0; opts->data_link_offset = 4; break; #endif case DLT_NULL: set_direction = 0; opts->data_link_offset = 4; break; default: opts->data_link_offset = 0; break; } /* We are only interested on seeing packets coming into the interface. */ if ((opts->pcap_any_direction == 0) && (set_direction == 1) && (pcap_file_mode == 0) && (pcap_setdirection(pcap, PCAP_D_IN) < 0)) if(opts->verbose) log_msg(LOG_WARNING, "[*] Warning: pcap error on setdirection: %s.", pcap_geterr(pcap)); /* Set our pcap handle nonblocking mode. * * NOTE: This is simply set to 0 for now until we find a need * to actually use this mode (which when set on a FreeBSD * system, it silently breaks the packet capture). */ if((pcap_file_mode == 0) && (pcap_setnonblock(pcap, DEF_PCAP_NONBLOCK, errstr)) == -1) { log_msg(LOG_ERR, "[*] Error setting pcap nonblocking to %i: %s", 0, errstr ); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } pcap_dispatch_count = strtol_wrapper(opts->config[CONF_PCAP_DISPATCH_COUNT], 0, RCHK_MAX_PCAP_DISPATCH_COUNT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid PCAP_DISPATCH_COUNT"); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } /* Initialize our signal handlers. You can check the return value for * the number of signals that were *not* set. Those that were not set * will be listed in the log/stderr output. */ if(set_sig_handlers() > 0) log_msg(LOG_ERR, "Errors encountered when setting signal handlers."); log_msg(LOG_INFO, "Starting fwknopd main event loop."); /* Jump into our home-grown packet cature loop. */ while(1) { /* If we got a SIGCHLD and it was the tcp server, then handle it here. */ if(got_sigchld) { if(opts->tcp_server_pid > 0) { child_pid = waitpid(0, &status, WNOHANG); if(child_pid == opts->tcp_server_pid) { if(WIFSIGNALED(status)) log_msg(LOG_WARNING, "TCP server got signal: %i", WTERMSIG(status)); log_msg(LOG_WARNING, "TCP server exited with status of %i. Attempting restart.", WEXITSTATUS(status) ); opts->tcp_server_pid = 0; /* Attempt to restart tcp server ? */ usleep(1000000); run_tcp_server(opts); } } got_sigchld = 0; } /* Any signal except USR1, USR2, and SIGCHLD mean break the loop. */ if(got_signal != 0) { if(got_sigint || got_sigterm || got_sighup) { pcap_breakloop(pcap); pending_break = 1; } else if(got_sigusr1 || got_sigusr2) { /* Not doing anything with these yet. */ got_sigusr1 = got_sigusr2 = 0; got_signal = 0; } else got_signal = 0; } res = pcap_dispatch(pcap, pcap_dispatch_count, (pcap_handler)&process_packet, (unsigned char *)opts); /* Count processed packets */ if(res > 0) { if(opts->foreground == 1 && opts->verbose > 2) log_msg(LOG_DEBUG, "pcap_dispatch() processed: %d packets", res); /* Count the set of processed packets (pcap_dispatch() return * value) - we use this as a comparison for --packet-limit regardless * of SPA packet validity at this point. */ opts->packet_ctr += res; if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit) { log_msg(LOG_WARNING, "* Incoming packet count limit of %i reached", opts->packet_ctr_limit ); pcap_breakloop(pcap); pending_break = 1; } } /* If there was an error, complain and go on (to an extent before * giving up). */ else if(res == -1) { log_msg(LOG_ERR, "[*] Error from pcap_dispatch: %s", pcap_geterr(pcap) ); if(pcap_errcnt++ > MAX_PCAP_ERRORS_BEFORE_BAIL) { log_msg(LOG_ERR, "[*] %i consecutive pcap errors. Giving up", pcap_errcnt ); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } } else if(pending_break == 1 || res == -2) { /* pcap_breakloop was called, so we bail. */ log_msg(LOG_INFO, "Gracefully leaving the fwknopd event loop."); break; } else pcap_errcnt = 0; /* Check for any expired firewall rules and deal with them. */ check_firewall_rules(opts); #if FIREWALL_IPFW /* Purge expired rules that no longer have any corresponding * dynamic rules. */ if(opts->fw_config->total_rules > 0) { time(&now); if(opts->fw_config->last_purge < (now - opts->fw_config->purge_interval)) { ipfw_purge_expired_rules(opts); opts->fw_config->last_purge = now; } } #endif usleep(useconds); } pcap_close(pcap); return(0); }
/* 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; }
/* Decode the encoded SPA data. */ int fko_decode_spa_data(fko_ctx_t ctx) { char *tbuf, *ndx, *tmp; int t_size, i, is_err; if (! is_valid_encoded_msg_len(ctx->encoded_msg_len)) return(FKO_ERROR_INVALID_DATA_DECODE_MSGLEN_VALIDFAIL); /* Make sure there are no non-ascii printable chars */ for (i=0; i < (int)strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE); i++) if(isprint(ctx->encoded_msg[i]) == 0) return(FKO_ERROR_INVALID_DATA_DECODE_NON_ASCII); /* Make sure there are enough fields in the SPA packet * delimited with ':' chars */ ndx = ctx->encoded_msg; for (i=0; i < MAX_SPA_FIELDS; i++) { if ((tmp = strchr(ndx, ':')) == NULL) break; ndx = tmp; ndx++; } if (i < MIN_SPA_FIELDS) return(FKO_ERROR_INVALID_DATA_DECODE_LT_MIN_FIELDS); t_size = strnlen(ndx, SHA512_B64_LEN+1); switch(t_size) { case MD5_B64_LEN: ctx->digest_type = FKO_DIGEST_MD5; ctx->digest_len = MD5_B64_LEN; break; case SHA1_B64_LEN: ctx->digest_type = FKO_DIGEST_SHA1; ctx->digest_len = SHA1_B64_LEN; break; case SHA256_B64_LEN: ctx->digest_type = FKO_DIGEST_SHA256; ctx->digest_len = SHA256_B64_LEN; break; case SHA384_B64_LEN: ctx->digest_type = FKO_DIGEST_SHA384; ctx->digest_len = SHA384_B64_LEN; break; case SHA512_B64_LEN: ctx->digest_type = FKO_DIGEST_SHA512; ctx->digest_len = SHA512_B64_LEN; break; default: /* Invalid or unsupported digest */ return(FKO_ERROR_INVALID_DIGEST_TYPE); } if (ctx->encoded_msg_len - t_size < 0) return(FKO_ERROR_INVALID_DATA_DECODE_ENC_MSG_LEN_MT_T_SIZE); if(ctx->digest != NULL) free(ctx->digest); /* Copy the digest into the context and terminate the encoded data * at that point so the original digest is not part of the * encoded string. */ ctx->digest = strdup(ndx); if(ctx->digest == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* Zero out the rest of the encoded_msg bucket... */ bzero((ndx-1), t_size); ctx->encoded_msg_len -= t_size+1; /* Make a tmp bucket for processing base64 encoded data and * other general use. */ tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE); if(tbuf == NULL) return(FKO_ERROR_MEMORY_ALLOCATION); /* Can now verify the digest. */ switch(ctx->digest_type) { case FKO_DIGEST_MD5: md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len); break; case FKO_DIGEST_SHA1: sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len); break; case FKO_DIGEST_SHA256: sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len); break; case FKO_DIGEST_SHA384: sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len); break; case FKO_DIGEST_SHA512: sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len); break; } /* We give up here if the computed digest does not match the * digest in the message data. */ if(constant_runtime_cmp(ctx->digest, tbuf, t_size) != 0) { free(tbuf); return(FKO_ERROR_DIGEST_VERIFICATION_FAILED); } /* Now we will work through the encoded data and extract (and base64- * decode where necessary), the SPA data fields and populate the context. */ ndx = ctx->encoded_msg; /* The rand val data */ if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_RAND_MISSING); } if(ctx->rand_val != NULL) free(ctx->rand_val); ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1); if(ctx->rand_val == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE); /* Jump to the next field (username). We need to use the temp buffer * for the base64 decode step. */ ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_MISSING); } if (t_size > MAX_SPA_USERNAME_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_TOOBIG); } strlcpy(tbuf, ndx, t_size+1); if(ctx->username != NULL) free(ctx->username); ctx->username = malloc(t_size+1); /* Yes, more than we need */ if(ctx->username == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } if(b64_decode(tbuf, (unsigned char*)ctx->username) < 0) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_DECODEFAIL); } if(validate_username(ctx->username) != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_VALIDFAIL); } /* Extract the timestamp value. */ ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING); } if (t_size > MAX_SPA_TIMESTAMP_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG); } strlcpy(tbuf, ndx, t_size+1); ctx->timestamp = (unsigned int) strtol_wrapper(tbuf, 0, -1, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL); } /* Extract the version string. */ ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_MISSING); } if (t_size > MAX_SPA_VERSION_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_TOOBIG); } if(ctx->version != NULL) free(ctx->version); ctx->version = malloc(t_size+1); if(ctx->version == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } strlcpy(ctx->version, ndx, t_size+1); /* Extract the message type value. */ ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING); } if (t_size > MAX_SPA_MESSAGE_TYPE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG); } strlcpy(tbuf, ndx, t_size+1); ctx->message_type = strtol_wrapper(tbuf, 0, FKO_LAST_MSG_TYPE, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL); } /* Extract the SPA message string. */ ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_MISSING); } if (t_size > MAX_SPA_MESSAGE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_TOOBIG); } strlcpy(tbuf, ndx, t_size+1); if(ctx->message != NULL) free(ctx->message); ctx->message = malloc(t_size+1); /* Yes, more than we need */ if(ctx->message == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } if(b64_decode(tbuf, (unsigned char*)ctx->message) < 0) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_DECODEFAIL); } if(ctx->message_type == FKO_COMMAND_MSG) { /* Require a message similar to: 1.2.3.4,<command> */ if(validate_cmd_msg(ctx->message) != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_VALIDFAIL); } } else { /* Require a message similar to: 1.2.3.4,tcp/22 */ if(validate_access_msg(ctx->message) != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_ACCESS_VALIDFAIL); } } /* Extract nat_access string if the message_type indicates so. */ if( ctx->message_type == FKO_NAT_ACCESS_MSG || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) { ndx += t_size + 1; if((t_size = strcspn(ndx, ":")) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_MISSING); } if (t_size > MAX_SPA_MESSAGE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_TOOBIG); } strlcpy(tbuf, ndx, t_size+1); if(ctx->nat_access != NULL) free(ctx->nat_access); ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */ if(ctx->nat_access == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } if(b64_decode(tbuf, (unsigned char*)ctx->nat_access) < 0) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_DECODEFAIL); } if(validate_nat_access_msg(ctx->nat_access) != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_VALIDFAIL); } } /* Now look for a server_auth string. */ ndx += t_size + 1; if((t_size = strlen(ndx)) > 0) { if (t_size > MAX_SPA_MESSAGE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_MISSING); } /* There is data, but what is it? * If the message_type does not have a timeout, assume it is a * server_auth field. */ if( ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) { strlcpy(tbuf, ndx, t_size+1); if(ctx->server_auth != NULL) free(ctx->server_auth); ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */ if(ctx->server_auth == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_DECODEFAIL); } /* At this point we should be done. */ free(tbuf); /* Call the context initialized. */ ctx->initval = FKO_CTX_INITIALIZED; FKO_SET_CTX_INITIALIZED(ctx); return(FKO_SUCCESS); } /* If we are here then we may still have a server_auth string, * or a timeout, or both. So we look for a ':' delimiter. If * it is there we have both, if not we check the message_type * again. */ if(strchr(ndx, ':')) { t_size = strcspn(ndx, ":"); if (t_size > MAX_SPA_MESSAGE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_TOOBIG); } /* Looks like we have both, so assume this is the */ strlcpy(tbuf, ndx, t_size+1); if(ctx->server_auth != NULL) free(ctx->server_auth); ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */ if(ctx->server_auth == NULL) { free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); } if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_DECODEFAIL); } ndx += t_size + 1; } /* Now we look for a timeout value if one is supposed to be there. */ if( ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG) { if((t_size = strlen(ndx)) < 1) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING); } if (t_size > MAX_SPA_MESSAGE_SIZE) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG); } /* Should be a number only. */ if(strspn(ndx, "0123456789") != t_size) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL); } ctx->client_timeout = (unsigned int) strtol_wrapper(ndx, 0, (2 << 15), NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL); } } } /* Done with the tmp buffer. */ free(tbuf); /* Call the context initialized. */ ctx->initval = FKO_CTX_INITIALIZED; FKO_SET_CTX_INITIALIZED(ctx); return(FKO_SUCCESS); }
int run_udp_server(fko_srv_options_t *opts) { int s_sock, sfd_flags, selval, pkt_len; int is_err, s_timeout, rv=1, chk_rm_all=0; int rules_chk_threshold; fd_set sfd_set; struct sockaddr_in saddr, caddr; struct timeval tv; char sipbuf[MAX_IPV4_STR_LEN] = {0}; char dgram_msg[MAX_SPA_PACKET_LEN+1] = {0}; unsigned short port; socklen_t clen; port = strtol_wrapper(opts->config[CONF_UDPSERV_PORT], 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] Invalid max UDPSERV_PORT value."); return -1; } s_timeout = strtol_wrapper(opts->config[CONF_UDPSERV_SELECT_TIMEOUT], 1, RCHK_MAX_UDPSERV_SELECT_TIMEOUT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] Invalid max UDPSERV_SELECT_TIMEOUT value."); return -1; } rules_chk_threshold = strtol_wrapper(opts->config[CONF_RULES_CHECK_THRESHOLD], 0, RCHK_MAX_RULES_CHECK_THRESHOLD, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid RULES_CHECK_THRESHOLD"); clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); } log_msg(LOG_INFO, "Kicking off UDP server to listen on port %i.", port); /* Now, let's make a UDP server */ if ((s_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { log_msg(LOG_ERR, "run_udp_server: socket() failed: %s", strerror(errno)); return -1; } /* Make our main socket non-blocking so we don't have to be stuck on * listening for incoming datagrams. */ if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0) { log_msg(LOG_ERR, "run_udp_server: fcntl F_GETFL error: %s", strerror(errno)); close(s_sock); return -1; } sfd_flags |= O_NONBLOCK; if(fcntl(s_sock, F_SETFL, sfd_flags) < 0) { log_msg(LOG_ERR, "run_udp_server: fcntl F_SETFL error setting O_NONBLOCK: %s", strerror(errno)); close(s_sock); return -1; } /* Construct local address structure */ memset(&saddr, 0x0, sizeof(saddr)); saddr.sin_family = AF_INET; /* Internet address family */ saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ saddr.sin_port = htons(port); /* Local port */ /* Bind to the local address */ if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { log_msg(LOG_ERR, "run_udp_server: bind() failed: %s", strerror(errno)); close(s_sock); return -1; } /* Initialize our signal handlers. You can check the return value for * the number of signals that were *not* set. Those that were not set * will be listed in the log/stderr output. */ if(set_sig_handlers() > 0) log_msg(LOG_ERR, "Errors encountered when setting signal handlers."); FD_ZERO(&sfd_set); /* Now loop and receive SPA packets */ while(1) { if(sig_do_stop()) { if(opts->verbose) log_msg(LOG_INFO, "udp_server: terminating signal received, will stop."); break; } /* Check for any expired firewall rules and deal with them. */ if(!opts->test) { if(rules_chk_threshold > 0) { opts->check_rules_ctr++; if ((opts->check_rules_ctr % rules_chk_threshold) == 0) { chk_rm_all = 1; opts->check_rules_ctr = 0; } } check_firewall_rules(opts, chk_rm_all); chk_rm_all = 0; } /* Initialize and setup the socket for select. */ FD_SET(s_sock, &sfd_set); /* Set our select timeout to (500ms by default). */ tv.tv_sec = 0; tv.tv_usec = s_timeout; selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv); if(selval == -1) { if(errno == EINTR) { /* restart loop but only after we check for a terminating * signal above in sig_do_stop() */ continue; } else { log_msg(LOG_ERR, "run_udp_server: select error socket: %s", strerror(errno)); rv = -1; break; } } if(selval == 0) continue; if(! FD_ISSET(s_sock, &sfd_set)) continue; /* If we make it here then there is a datagram to process */ clen = sizeof(caddr); pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, 0, (struct sockaddr *)&caddr, &clen); dgram_msg[pkt_len] = 0x0; if(opts->verbose) { memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); log_msg(LOG_INFO, "udp_server: Got UDP datagram (%d bytes) from: %s", pkt_len, sipbuf); } /* Expect the data to not be too large */ if(pkt_len <= MAX_SPA_PACKET_LEN) { /* Copy the packet for SPA processing */ strlcpy((char *)opts->spa_pkt.packet_data, dgram_msg, pkt_len+1); opts->spa_pkt.packet_data_len = pkt_len; opts->spa_pkt.packet_proto = IPPROTO_UDP; opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr; opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr; opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port); opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port); incoming_spa(opts); } memset(dgram_msg, 0x0, sizeof(dgram_msg)); opts->packet_ctr += 1; if(opts->foreground == 1 && opts->verbose > 2) log_msg(LOG_DEBUG, "run_udp_server() processed: %d packets", opts->packet_ctr); if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit) { log_msg(LOG_WARNING, "* Incoming packet count limit of %i reached", opts->packet_ctr_limit ); break; } } /* infinite while loop */ close(s_sock); return rv; }
/* 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); }
/* Fork off and run a "dummy" TCP server. The return value is the PID of * the child process or -1 if there is a fork error. */ int run_tcp_server(fko_srv_options_t *opts) { pid_t pid, ppid; int s_sock, c_sock, sfd_flags, clen, selval; int reuse_addr = 1, is_err; fd_set sfd_set; struct sockaddr_in saddr, caddr; struct timeval tv; char sipbuf[MAX_IPV4_STR_LEN] = {0}; unsigned short port; port = strtol_wrapper(opts->config[CONF_TCPSERV_PORT], 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] Invalid max TCPSERV_PORT value."); exit(EXIT_FAILURE); } log_msg(LOG_INFO, "Kicking off TCP server to listen on port %i.", port); /* Fork off a child process to run the command and provide its outputs. */ pid = fork(); /* Non-zero pid means we are the parent or there was a fork error. * in either case we simply return that value to the caller. */ if (pid != 0) { opts->tcp_server_pid = pid; return(pid); } /* Get our parent PID so we can periodically check for it. We want to * know when it goes away so we can to. */ ppid = getppid(); /* We are the child. The first thing to do is close our copy of the * parent PID file so we don't end up holding the lock if the parent * suffers a sudden death that doesn't take us out too. */ close(opts->lock_fd); /* Now, let's make a TCP server */ if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { log_msg(LOG_ERR, "run_tcp_server: socket() failed: %s", strerror(errno)); exit(EXIT_FAILURE); } /* So that we can re-bind to it without TIME_WAIT problems */ if(setsockopt(s_sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1) { log_msg(LOG_ERR, "run_tcp_server: setsockopt error: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Make our main socket non-blocking so we don't have to be stuck on * listening for incoming connections. */ if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0) { log_msg(LOG_ERR, "run_tcp_server: fcntl F_GETFL error: %s", strerror(errno)); exit(EXIT_FAILURE); } sfd_flags |= O_NONBLOCK; if(fcntl(s_sock, F_SETFL, sfd_flags) < 0) { log_msg(LOG_ERR, "run_tcp_server: fcntl F_SETFL error setting O_NONBLOCK: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Construct local address structure */ memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; /* Internet address family */ saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ saddr.sin_port = htons(port); /* Local port */ /* Bind to the local address */ if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { log_msg(LOG_ERR, "run_tcp_server: bind() failed: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Mark the socket so it will listen for incoming connections * (but only one at a time) */ if (listen(s_sock, 1) < 0) { log_msg(LOG_ERR, "run_tcp_server: listen() failed: %s", strerror(errno)); exit(EXIT_FAILURE); } clen = sizeof(caddr); /* Now loop and accept and drop connections after the first packet or a * short timeout. */ while(1) { /* Initialize and setup the socket for select. */ FD_ZERO(&sfd_set); FD_SET(s_sock, &sfd_set); /* Set our select timeout to 200 ms. */ tv.tv_sec = 0; tv.tv_usec = 200000; selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv); if(selval == -1) { /* Select error - so kill the child and bail. */ log_msg(LOG_ERR, "run_tcp_server: select error socket: %s", strerror(errno)); exit(EXIT_FAILURE); } if(selval == 0) { /* Timeout - So we check to make sure our parent is still there by simply * using kill(ppid, 0) and checking the return value. */ if(kill(ppid, 0) != 0 && errno == ESRCH) exit(EXIT_FAILURE); continue; } /* Wait for a client to connect */ if((c_sock = accept(s_sock, (struct sockaddr *) &caddr, (socklen_t *)&clen)) < 0) { log_msg(LOG_ERR, "run_tcp_server: accept() failed: %s", strerror(errno)); exit(EXIT_FAILURE); /* Should this be fatal? */ } if(opts->verbose) { memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); log_msg(LOG_INFO, "tcp_server: Got TCP connection from %s.", sipbuf); } /* Though hacky and clunky, we just sleep for a second then * close the socket. No need to read or write anything. This * just gives the client a sufficient window to send their * request on this socket. In any case the socket is closed * after that time. */ usleep(1000000); shutdown(c_sock, SHUT_RDWR); close(c_sock); } }
static void options_parse(int argc, char *argv[]) { int option_index= 0; int option_rv; memcached_programs_help_st help_options[]= { {0}, }; static struct option long_options[]= { {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION}, {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP}, {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE}, {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG}, {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS}, {(OPTIONSTRING)"flag", required_argument, NULL, OPT_FLAG}, {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE}, {(OPTIONSTRING)"set", no_argument, NULL, OPT_SET}, {(OPTIONSTRING)"add", no_argument, NULL, OPT_ADD}, {(OPTIONSTRING)"replace", no_argument, NULL, OPT_REPLACE}, {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH}, {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY}, {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME}, {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD}, {0, 0, 0, 0}, }; while (1) { option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index); if (option_rv == -1) break; switch (option_rv) { case 0: break; case OPT_BINARY: opt_binary = 1; break; case OPT_VERBOSE: /* --verbose or -v */ opt_verbose = OPT_VERBOSE; break; case OPT_DEBUG: /* --debug or -d */ opt_verbose = OPT_DEBUG; break; case OPT_VERSION: /* --version or -V */ version_command(PROGRAM_NAME); break; case OPT_HELP: /* --help or -h */ help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options); break; case OPT_SERVERS: /* --servers or -s */ opt_servers= strdup(optarg); break; case OPT_FLAG: /* --flag */ { bool strtol_error; opt_flags= (uint32_t)strtol_wrapper(optarg, 16, &strtol_error); if (strtol_error == true) { fprintf(stderr, "Bad value passed via --flag\n"); exit(1); } break; } case OPT_EXPIRE: /* --expire */ { bool strtol_error; opt_expires= (time_t)strtol_wrapper(optarg, 16, &strtol_error); if (strtol_error == true) { fprintf(stderr, "Bad value passed via --flag\n"); exit(1); } } case OPT_SET: opt_method= OPT_SET; break; case OPT_REPLACE: opt_method= OPT_REPLACE; break; case OPT_ADD: opt_method= OPT_ADD; break; case OPT_HASH: opt_hash= strdup(optarg); break; case OPT_USERNAME: opt_username= optarg; break; case OPT_PASSWD: opt_passwd= optarg; break; case '?': /* getopt_long already printed an error message. */ exit(1); default: abort(); } } }
/* Initialize program configuration via config file and/or command-line * switches. */ void config_init(fko_srv_options_t *opts, int argc, char **argv) { int cmd_arg, index, is_err; unsigned char got_conf_file = 0, got_override_config = 0; char override_file[MAX_LINE_LEN] = {0}; char *ndx, *cmrk; /* Zero out options and opts_track. */ memset(opts, 0x00, sizeof(fko_srv_options_t)); /* Set some preconfiguration options (i.e. build-time defaults) */ set_preconfig_entries(opts); /* In case this is a re-config. */ optind = 0; /* First, scan the command-line args for -h/--help or an alternate * configuration file. If we find an alternate config file, use it, * otherwise use the default. We also grab any override config files * as well. */ while ((cmd_arg = getopt_long(argc, argv, GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) { /* If help is wanted, give it and exit. */ switch(cmd_arg) { case 'h': usage(); clean_exit(opts, NO_FW_CLEANUP, EXIT_SUCCESS); break; /* Look for configuration file arg. */ case 'c': set_config_entry(opts, CONF_CONFIG_FILE, optarg); got_conf_file++; /* If we already have the config_override option, we are done. */ if(got_override_config > 0) break; /* Look for override configuration file arg. */ case 'O': set_config_entry(opts, CONF_OVERRIDE_CONFIG, optarg); got_override_config++; /* If we already have the conf_file option, we are done. */ if(got_conf_file > 0) break; } } /* If no alternate configuration file was specified, we use the * default. */ if(opts->config[CONF_CONFIG_FILE] == NULL) set_config_entry(opts, CONF_CONFIG_FILE, DEF_CONFIG_FILE); /* Parse configuration file to populate any params not already specified * via command-line options. */ parse_config_file(opts, opts->config[CONF_CONFIG_FILE]); /* If there are override configuration entries, process them * here. */ if(opts->config[CONF_OVERRIDE_CONFIG] != NULL) { /* Make a copy of the override_config string so we can munge it. */ strlcpy(override_file, opts->config[CONF_OVERRIDE_CONFIG], sizeof(override_file)); ndx = override_file; cmrk = strchr(ndx, ','); if(cmrk == NULL) { /* Only one to process... */ parse_config_file(opts, ndx); } else { /* Walk the string pulling the next config override * at the comma delimiters. */ while(cmrk != NULL) { *cmrk = '\0'; parse_config_file(opts, ndx); ndx = cmrk + 1; cmrk = strchr(ndx, ','); } /* Process the last entry */ parse_config_file(opts, ndx); } } /* Set up the verbosity level according to the value found in the * config files */ if (opts->config[CONF_VERBOSE] != NULL) { opts->verbose = strtol_wrapper(opts->config[CONF_VERBOSE], 0, -1, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] VERBOSE value '%s' not in the range (>0)", opts->config[CONF_VERBOSE]); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } } /* Reset the options index so we can run through them again. */ optind = 0; /* Last, but not least, we process command-line options (some of which * may override configuration file options. */ while ((cmd_arg = getopt_long(argc, argv, GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) { switch(cmd_arg) { case 'a': set_config_entry(opts, CONF_ACCESS_FILE, optarg); break; case 'c': /* This was handled earlier */ break; case 'C': opts->packet_ctr_limit = strtol_wrapper(optarg, 0, (2 << 30), NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid -C packet count limit '%s'", optarg); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } break; case 'd': #if USE_FILE_CACHE set_config_entry(opts, CONF_DIGEST_FILE, optarg); #else set_config_entry(opts, CONF_DIGEST_DB_FILE, optarg); #endif break; case 'D': opts->dump_config = 1; break; case 'f': opts->foreground = 1; break; case FW_LIST: opts->fw_list = 1; break; case FW_LIST_ALL: opts->fw_list = 1; opts->fw_list_all = 1; break; case FW_FLUSH: opts->fw_flush = 1; break; case GPG_HOME_DIR: if (is_valid_dir(optarg)) { set_config_entry(opts, CONF_GPG_HOME_DIR, optarg); } else { log_msg(LOG_ERR, "[*] Directory '%s' could not stat()/does not exist?", optarg); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } break; case 'i': set_config_entry(opts, CONF_PCAP_INTF, optarg); break; case 'K': opts->kill = 1; break; case 'l': set_config_entry(opts, CONF_LOCALE, optarg); break; case 'O': /* This was handled earlier */ break; case 'p': set_config_entry(opts, CONF_FWKNOP_PID_FILE, optarg); break; case 'P': set_config_entry(opts, CONF_PCAP_FILTER, optarg); break; case PCAP_FILE: set_config_entry(opts, CONF_PCAP_FILE, optarg); break; case ENABLE_PCAP_ANY_DIRECTION: opts->pcap_any_direction = 1; break; case ROTATE_DIGEST_CACHE: opts->rotate_digest_cache = 1; break; case 'R': opts->restart = 1; break; case 'S': opts->status = 1; break; /* Verbosity level */ case 'v': opts->verbose++; break; case SYSLOG_ENABLE: opts->syslog_enable = 1; break; case 'V': fprintf(stdout, "fwknopd server %s\n", MY_VERSION); clean_exit(opts, NO_FW_CLEANUP, EXIT_SUCCESS); break; default: usage(); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } } /* Now that we have all of our options set, and we are actually going to * start fwknopd, we can validate them. */ validate_options(opts); return; }
static int parse_url(char *res_url, struct url* url) { char *s_ndx, *e_ndx; int tlen, tlen_offset, port, is_err; /* https is not supported. */ if(strncasecmp(res_url, "https", 5) == 0) { log_msg(LOG_VERBOSITY_ERROR, "[*] https is not yet supported for http-resolve-ip."); return(-1); } /* Strip off http:// portion if necessary */ if(strncasecmp(res_url, "http://", 7) == 0) s_ndx = res_url + 7; else s_ndx = res_url; /* Look for a colon in case an alternate port was specified. */ e_ndx = strchr(s_ndx, ':'); if(e_ndx != NULL) { port = strtol_wrapper(e_ndx+1, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_VERBOSITY_ERROR, "[*] resolve-url port value is invalid, must be in [%d-%d]", 1, MAX_PORT); return(-1); } snprintf(url->port, sizeof(url->port)-1, "%u", port); /* Get the offset we need to skip the port portion when we * extract the hostname part. */ tlen_offset = strlen(url->port)+1; } else { strlcpy(url->port, "80", sizeof(url->port)); tlen_offset = 0; } /* Get rid of any trailing slash */ if(res_url[strlen(res_url)-1] == '/') res_url[strlen(res_url)-1] = '\0'; e_ndx = strchr(s_ndx, '/'); if(e_ndx == NULL) tlen = strlen(s_ndx)+1; else tlen = (e_ndx-s_ndx)+1; tlen -= tlen_offset; if(tlen > MAX_URL_HOST_LEN) { log_msg(LOG_VERBOSITY_ERROR, "resolve-url hostname portion is too large."); return(-1); } strlcpy(url->host, s_ndx, tlen); if(e_ndx != NULL) { if(strlen(e_ndx) > MAX_URL_PATH_LEN) { log_msg(LOG_VERBOSITY_ERROR, "resolve-url path portion is too large."); return(-1); } strlcpy(url->path, e_ndx, sizeof(url->path)); } else { /* default to "GET /" if there isn't a more specific URL */ strlcpy(url->path, "/", sizeof(url->path)); } return(0); }
/* Iterate over the configure firewall access chains and purge expired * firewall rules. */ void check_firewall_rules(const fko_srv_options_t * const opts) { char exp_str[12] = {0}; char rule_num_str[6] = {0}; char *ndx, *rn_start, *rn_end, *tmp_mark; int i, res, rn_offset, rule_num, is_err; time_t now, rule_exp, min_exp = 0; struct fw_chain *ch = opts->fw_config->chain; time(&now); /* Iterate over each chain and look for active rules to delete. */ for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++) { /* If there are no active rules or we have not yet * reached our expected next expire time, continue. */ if(ch[i].active_rules == 0 || ch[i].next_expire > now) continue; zero_cmd_buffers(); rn_offset = 0; /* There should be a rule to delete. Get the current list of * rules for this chain and delete the ones that are expired. */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS, opts->fw_config->fw_command, ch[i].table, ch[i].to_chain ); res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, WANT_STDERR, NO_TIMEOUT, &pid_status, opts); chop_newline(cmd_out); log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, cmd_out: %s)", cmd_buf, res, cmd_out); if(!EXTCMD_IS_SUCCESS(res)) { log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out); continue; } log_msg(LOG_DEBUG, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out); ndx = strstr(cmd_out, EXPIRE_COMMENT_PREFIX); if(ndx == NULL) { /* we did not find an expected rule. */ log_msg(LOG_ERR, "Did not find expire comment in rules list %i", i); if (ch[i].active_rules > 0) ch[i].active_rules--; continue; } /* walk the list and process rules as needed. */ while (ndx != NULL) { /* Jump forward and extract the timestamp */ ndx += strlen(EXPIRE_COMMENT_PREFIX); /* remember this spot for when we look for the next * rule. */ tmp_mark = ndx; strlcpy(exp_str, ndx, sizeof(exp_str)); rule_exp = (time_t)atoll(exp_str); if(rule_exp <= now) { /* Backtrack and get the rule number and delete it. */ rn_start = ndx; while(--rn_start > cmd_out) { if(*rn_start == '\n') break; } if(*rn_start != '\n') { /* This should not happen. But if it does, complain, * decrement the active rule value, and go on. */ log_msg(LOG_ERR, "Rule parse error while finding rule line start in chain %i", i); if (ch[i].active_rules > 0) ch[i].active_rules--; break; } rn_start++; rn_end = strchr(rn_start, ' '); if(rn_end == NULL) { /* This should not happen. But if it does, complain, * decrement the active rule value, and go on. */ log_msg(LOG_ERR, "Rule parse error while finding rule number in chain %i", i); if (ch[i].active_rules > 0) ch[i].active_rules--; break; } strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1); rule_num = strtol_wrapper(rule_num_str, rn_offset, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "Rule parse error while finding rule number in chain %i", i); if (ch[i].active_rules > 0) ch[i].active_rules--; break; } zero_cmd_buffers(); snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, opts->fw_config->fw_command, ch[i].table, ch[i].to_chain, rule_num - rn_offset ); res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, NO_TIMEOUT, &pid_status, opts); chop_newline(err_buf); log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)", cmd_buf, res, err_buf); if(EXTCMD_IS_SUCCESS(res)) { log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u", rule_num_str, ch[i].to_chain, rule_exp ); rn_offset++; if (ch[i].active_rules > 0) ch[i].active_rules--; } else log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); } else { /* Track the minimum future rule expire time. */ if(rule_exp > now) min_exp = (min_exp < rule_exp) ? min_exp : rule_exp; } /* Push our tracking index forward beyond (just processed) _exp_ * string so we can continue to the next rule in the list. */ ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX); } /* Set the next pending expire time accordingly. 0 if there are no * more rules, or whatever the next expected (min_exp) time will be. */ if(ch[i].active_rules < 1) ch[i].next_expire = 0; else if(min_exp) ch[i].next_expire = min_exp; } }
static int set_fw_chain_conf(const int type, const char * const conf_str) { int i, j, is_err; char tbuf[MAX_LINE_LEN] = {0}; const char *ndx = conf_str; char *chain_fields[FW_NUM_CHAIN_FIELDS]; struct fw_chain *chain = &(fwc.chain[type]); if(conf_str == NULL) { log_msg(LOG_ERR, "[*] NULL conf_str"); return 0; } chain->type = type; if(ndx != NULL) chain_fields[0] = tbuf; i = 0; j = 1; while(*ndx != '\0') { if(*ndx != ' ') { if(*ndx == ',') { tbuf[i] = '\0'; chain_fields[j++] = &(tbuf[++i]); } else tbuf[i++] = *ndx; } if(*ndx != '\0' && *ndx != ' ' && *ndx != ',' && *ndx != '_' && isalnum(*ndx) == 0) { log_msg(LOG_ERR, "[*] Custom chain config parse error: " "invalid character '%c' for chain type %i, " "line: %s", *ndx, type, conf_str); return 0; } ndx++; } /* Sanity check - j should be the number of chain fields * (excluding the type). */ if(j != FW_NUM_CHAIN_FIELDS) { log_msg(LOG_ERR, "[*] Custom chain config parse error: " "wrong number of fields for chain type %i, " "line: %s", type, conf_str); return 0; } /* Pull and set Target */ strlcpy(chain->target, chain_fields[0], sizeof(chain->target)); /* Pull and set Table */ strlcpy(chain->table, chain_fields[1], sizeof(chain->table)); /* Pull and set From_chain */ strlcpy(chain->from_chain, chain_fields[2], sizeof(chain->from_chain)); /* Pull and set Jump_rule_position */ chain->jump_rule_pos = strtol_wrapper(chain_fields[3], 0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid jump rule position in Line: %s", conf_str); return 0; } /* Pull and set To_chain */ strlcpy(chain->to_chain, chain_fields[4], sizeof(chain->to_chain)); /* Pull and set to_chain rule position */ chain->rule_pos = strtol_wrapper(chain_fields[5], 0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_ERR, "[*] invalid to_chain rule position in Line: %s", conf_str); return 0; } return 1; }
/* 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; }
/* Set NAT access string */ static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const access_buf) { char nat_access_buf[MAX_LINE_LEN] = {0}; char tmp_access_port[MAX_PORT_STR_LEN+1] = {0}, *ndx = NULL; int access_port = 0, i = 0, is_err = 0; char hostname[HOSTNAME_BUFSIZE] = {0}; int port = 0; struct addrinfo hints; memset(&hints, 0 , sizeof(hints)); ndx = strchr(options->access_str, '/'); if(ndx == NULL) { log_msg(LOG_VERBOSITY_ERROR, "[*] Expecting <proto>/<port> for -A arg."); return FKO_ERROR_INVALID_DATA; } ndx++; while(*ndx != '\0' && isdigit(*ndx) && i < MAX_PORT_STR_LEN) { tmp_access_port[i] = *ndx; ndx++; i++; } tmp_access_port[i] = '\0'; access_port = strtol_wrapper(tmp_access_port, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err); if(is_err != FKO_SUCCESS) { log_msg(LOG_VERBOSITY_ERROR, "[*] Invalid port value '%d' for -A arg.", access_port); return FKO_ERROR_INVALID_DATA; } if (options->nat_local && options->nat_access_str[0] == 0x0) { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->spa_server_str, access_port); } if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0) { if (ipv4_str_has_port(options->nat_access_str)) { snprintf(nat_access_buf, MAX_LINE_LEN, "%s", options->nat_access_str); } else { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->nat_access_str, access_port); } } /* Check if there is a hostname to resolve as an ip address in the NAT access buffer */ if (is_hostname_str_with_port(nat_access_buf, hostname, sizeof(hostname), &port)) { /* We now send the hostname, and resolve it server side */ snprintf(nat_access_buf, MAX_LINE_LEN, "%s", options->nat_access_str); } /* assume just hostname */ else { snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE, options->nat_access_str, access_port); } if(options->nat_rand_port) { /* Must print to stdout what the random port is since * if not then the user will not which port will be * opened/NAT'd on the fwknopd side */ log_msg(LOG_VERBOSITY_NORMAL, "[+] Randomly assigned port '%d' on: '%s' will grant access to: '%s'", options->nat_port, access_buf, nat_access_buf); } return fko_set_spa_nat_access(ctx, nat_access_buf); }