/* Given a config parameter name, return its index or -1 if not found. */ static int config_entry_index(const fko_srv_options_t *opts, const char *var) { int i; for(i=0; i<NUMBER_OF_CONFIG_ENTRIES; i++) if(opts->config[i] != NULL && CONF_VAR_IS(var, config_map[i])) return(i); return(-1); }
/* Read and parse the access file, popluating the access data as we go. */ void parse_access_file(fko_srv_options_t *opts) { FILE *file_ptr; char *ndx; int got_source = 0; unsigned int num_lines = 0; char access_line_buf[MAX_LINE_LEN] = {0}; char var[MAX_LINE_LEN] = {0}; char val[MAX_LINE_LEN] = {0}; struct passwd *pw; struct stat st; acc_stanza_t *curr_acc = NULL; /* First see if the access file exists. If it doesn't, complain * and bail. */ if(stat(opts->config[CONF_ACCESS_FILE], &st) != 0) { fprintf(stderr, "[*] Access file: '%s' was not found.\n", opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } if ((file_ptr = fopen(opts->config[CONF_ACCESS_FILE], "r")) == NULL) { fprintf(stderr, "[*] Could not open access file: %s\n", opts->config[CONF_ACCESS_FILE]); perror(NULL); exit(EXIT_FAILURE); } /* Initialize the access list. */ acc_stanza_init(opts); /* Now walk through access file pulling the access entries into the * current stanza. */ while ((fgets(access_line_buf, MAX_LINE_LEN, file_ptr)) != NULL) { num_lines++; access_line_buf[MAX_LINE_LEN-1] = '\0'; /* Get past comments and empty lines (note: we only look at the * first character. */ if(IS_EMPTY_LINE(access_line_buf[0])) continue; if(sscanf(access_line_buf, "%s %[^;\n\r]", var, val) != 2) { fprintf(stderr, "*Invalid access file entry in %s at line %i.\n - '%s'", opts->config[CONF_ACCESS_FILE], num_lines, access_line_buf ); continue; } /* Remove any colon that may be on the end of the var */ if((ndx = strrchr(var, ':')) != NULL) *ndx = '\0'; /* */ if(opts->verbose > 3) fprintf(stderr, "ACCESS FILE: %s, LINE: %s\tVar: %s, Val: '%s'\n", opts->config[CONF_ACCESS_FILE], access_line_buf, var, val ); /* Process the entry. * * NOTE: If a new access.conf parameter is created. It also needs * to be accounted for in the following if/if else construct. */ if(CONF_VAR_IS(var, "SOURCE")) { /* If this is not the first stanza, sanity check the previous * stanza for the minimum required data. */ if(curr_acc != NULL) { if(!acc_data_is_valid(curr_acc)) { fprintf(stderr, "[*] Data error in access file: '%s'\n", opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } } /* Start new stanza. */ curr_acc = acc_stanza_add(opts); add_acc_string(&(curr_acc->source), val); got_source++; } else if (curr_acc == NULL) { /* The stanza must start with the "SOURCE" variable */ continue; } else if(CONF_VAR_IS(var, "OPEN_PORTS")) { add_acc_string(&(curr_acc->open_ports), val); } else if(CONF_VAR_IS(var, "RESTRICT_PORTS")) { add_acc_string(&(curr_acc->restrict_ports), val); } else if(CONF_VAR_IS(var, "KEY")) { if(strcasecmp(val, "__CHANGEME__") == 0) { fprintf(stderr, "[*] KEY value is not properly set in stanza source '%s' in access file: '%s'\n", curr_acc->source, opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } add_acc_string(&(curr_acc->key), val); } else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT")) { add_acc_int(&(curr_acc->fw_access_timeout), val); } else if(CONF_VAR_IS(var, "ENABLE_CMD_EXEC")) { add_acc_bool(&(curr_acc->enable_cmd_exec), val); } else if(CONF_VAR_IS(var, "CMD_EXEC_USER")) { add_acc_string(&(curr_acc->cmd_exec_user), val); errno = 0; pw = getpwnam(val); if(pw == NULL) { fprintf(stderr, "Unable to determine UID for CMD_EXEC_USER: %s.\n", errno ? strerror(errno) : "Not a user on this system"); exit(EXIT_FAILURE); } curr_acc->cmd_exec_uid = pw->pw_uid; } else if(CONF_VAR_IS(var, "REQUIRE_USERNAME")) { add_acc_string(&(curr_acc->require_username), val); } else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS")) { add_acc_bool(&(curr_acc->require_source_address), val); } else if(CONF_VAR_IS(var, "GPG_HOME_DIR")) { if (is_valid_dir(val)) { add_acc_string(&(curr_acc->gpg_home_dir), val); } else { fprintf(stderr, "[*] GPG_HOME_DIR directory '%s' stat()/existence problem in stanza source '%s' in access file: '%s'\n", val, curr_acc->source, opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } } else if(CONF_VAR_IS(var, "GPG_DECRYPT_ID")) { add_acc_string(&(curr_acc->gpg_decrypt_id), val); } else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW")) { if(strcasecmp(val, "__CHANGEME__") == 0) { fprintf(stderr, "[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'\n", curr_acc->source, opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } add_acc_string(&(curr_acc->gpg_decrypt_pw), val); } else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG")) { add_acc_bool(&(curr_acc->gpg_require_sig), val); } else if(CONF_VAR_IS(var, "GPG_IGNORE_SIG_VERIFY_ERROR")) { add_acc_bool(&(curr_acc->gpg_ignore_sig_error), val); } else if(CONF_VAR_IS(var, "GPG_REMOTE_ID")) { add_acc_string(&(curr_acc->gpg_remote_id), val); } else { fprintf(stderr, "*Ignoring unknown access parameter: '%s' in %s\n", var, opts->config[CONF_ACCESS_FILE] ); } } fclose(file_ptr); /* Basic check to ensure that we got at least one SOURCE stanza with * a valid KEY defined (valid meaning it has a value that is not * "__CHANGEME__". */ if (got_source == 0) { fprintf(stderr, "[*] Could not find valid SOURCE stanza in access file: '%s'\n", opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } /* Sanity check the last stanza */ if(!acc_data_is_valid(curr_acc)) { fprintf(stderr, "[*] Data error in access file: '%s'\n", opts->config[CONF_ACCESS_FILE]); exit(EXIT_FAILURE); } /* Expand our the expandable fields into their respective data buckets. */ expand_acc_ent_lists(opts); /* Make sure default values are set where needed. * a default value. */ set_acc_defaults(opts); return; }
/* Parse the config file... */ static void parse_config_file(fko_srv_options_t *opts, const char *config_file) { FILE *cfile_ptr; unsigned int numLines = 0; unsigned int i, good_ent; int cndx; char conf_line_buf[MAX_LINE_LEN] = {0}; char var[MAX_LINE_LEN] = {0}; char val[MAX_LINE_LEN] = {0}; char tmp1[MAX_LINE_LEN] = {0}; char tmp2[MAX_LINE_LEN] = {0}; struct stat st; /* Make sure the config file exists. */ if(stat(config_file, &st) != 0) { log_msg(LOG_ERR, "[*] Config file: '%s' was not found.", config_file); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } if(verify_file_perms_ownership(config_file) != 1) clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); /* See the comment in the parse_access_file() function regarding security * here relative to a TOCTOU bug flagged by Coverity. */ if ((cfile_ptr = fopen(config_file, "r")) == NULL) { log_msg(LOG_ERR, "[*] Could not open config file: %s", config_file); perror(NULL); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } while ((fgets(conf_line_buf, MAX_LINE_LEN, cfile_ptr)) != NULL) { numLines++; conf_line_buf[MAX_LINE_LEN-1] = '\0'; /* Get past comments and empty lines (note: we only look at the * first character. */ if(IS_EMPTY_LINE(conf_line_buf[0])) continue; if(sscanf(conf_line_buf, "%s %[^;\n\r]", var, val) != 2) { log_msg(LOG_ERR, "*Invalid config file entry in %s at line %i.\n - '%s'", config_file, numLines, conf_line_buf ); continue; } /* fprintf(stderr, "CONF FILE: %s, LINE: %s\tVar: %s, Val: '%s'\n", config_file, conf_line_buf, var, val ); */ good_ent = 0; for(i=0; i<NUMBER_OF_CONFIG_ENTRIES; i++) { if(CONF_VAR_IS(config_map[i], var)) { /* First check to see if we need to do a varable expansion * on this value. Note: this only supports one expansion and * only if the value starts with the variable. */ if(*val == '$') { if(sscanf((val+1), "%[A-Z_]%s", tmp1, tmp2)) { if((cndx = config_entry_index(opts, tmp1)) >= 0) { strlcpy(val, opts->config[cndx], sizeof(val)); strlcat(val, tmp2, sizeof(val)); } } } set_config_entry(opts, i, val); good_ent++; break; } } if(good_ent == 0) log_msg(LOG_ERR, "[*] Ignoring unknown configuration parameter: '%s' in %s", var, config_file ); } fclose(cfile_ptr); return; }
static int parse_rc_param(fko_cli_options_t *options, const char *var, char * val) { int tmpint; /* Digest Type */ if(CONF_VAR_IS(var, "DIGEST_TYPE")) { tmpint = digest_strtoint(val); if(tmpint < 0) return(-1); else options->digest_type = tmpint; } /* Server protocol */ else if(CONF_VAR_IS(var, "SPA_SERVER_PROTO")) { tmpint = proto_strtoint(val); if(tmpint < 0) return(-1); else options->spa_proto = tmpint; } /* Server port */ else if(CONF_VAR_IS(var, "SPA_SERVER_PORT")) { tmpint = atoi(val); if(tmpint < 0 || tmpint > MAX_PORT) return(-1); else options->spa_dst_port = tmpint; } /* Source port */ else if(CONF_VAR_IS(var, "SPA_SOURCE_PORT")) { tmpint = atoi(val); if(tmpint < 0 || tmpint > MAX_PORT) return(-1); else options->spa_src_port = tmpint; } /* Firewall rule timeout */ else if(CONF_VAR_IS(var, "FW_TIMEOUT")) { tmpint = atoi(val); if(tmpint < 0) return(-1); else options->fw_timeout = tmpint; } /* Allow IP */ else if(CONF_VAR_IS(var, "ALLOW_IP")) { /* In case this was set previously */ options->resolve_ip_http = 0; /* use source, resolve, or an actual IP */ if(strcasecmp(val, "source") == 0) strlcpy(options->allow_ip_str, "0.0.0.0", 8); else if(strcasecmp(val, "resolve") == 0) options->resolve_ip_http = 1; else /* Assume IP address */ strlcpy(options->allow_ip_str, val, MAX_IPV4_STR_LEN); } /* Time Offset */ else if(CONF_VAR_IS(var, "TIME_OFFSET")) { if(val[0] == '-') { val++; options->time_offset_minus = parse_time_offset(val); } else options->time_offset_plus = parse_time_offset(val); } /* Use GPG ? */ else if(CONF_VAR_IS(var, "USE_GPG")) { if(val[0] == 'y' || val[0] == 'Y') options->use_gpg = 1; } /* Use GPG Agent ? */ else if(CONF_VAR_IS(var, "USE_GPG_AGENT")) { if(val[0] == 'y' || val[0] == 'Y') options->use_gpg_agent = 1; } /* GPG Recipient */ else if(CONF_VAR_IS(var, "GPG_RECIPIENT")) { strlcpy(options->gpg_recipient_key, val, MAX_GPG_KEY_ID); } /* GPG Signer */ else if(CONF_VAR_IS(var, "GPG_SIGNER")) { strlcpy(options->gpg_signer_key, val, MAX_GPG_KEY_ID); } /* GPG Homedir */ else if(CONF_VAR_IS(var, "GPG_HOMEDIR")) { strlcpy(options->gpg_home_dir, val, MAX_PATH_LEN); } /* Spoof User */ else if(CONF_VAR_IS(var, "SPOOF_USER")) { strlcpy(options->spoof_user, val, MAX_USERNAME_LEN); } /* Spoof Source IP */ else if(CONF_VAR_IS(var, "SPOOF_SOURCE_IP")) { strlcpy(options->spoof_ip_src_str, val, MAX_IPV4_STR_LEN); } /* ACCESS request */ else if(CONF_VAR_IS(var, "ACCESS")) { strlcpy(options->access_str, val, MAX_LINE_LEN); } /* SPA Server (destination) */ else if(CONF_VAR_IS(var, "SPA_SERVER")) { strlcpy(options->spa_server_str, val, MAX_SERVER_STR_LEN); } /* Rand port ? */ else if(CONF_VAR_IS(var, "RAND_PORT")) { if(val[0] == 'y' || val[0] == 'Y') options->rand_port = 1; } /* Key file */ else if(CONF_VAR_IS(var, "KEY_FILE")) { strlcpy(options->get_key_file, val, MAX_PATH_LEN); } /* NAT Access Request */ else if(CONF_VAR_IS(var, "NAT_ACCESS")) { strlcpy(options->nat_access_str, val, MAX_PATH_LEN); } /* HTTP User Agent */ else if(CONF_VAR_IS(var, "HTTP_USER_AGENT")) { strlcpy(options->http_user_agent, val, HTTP_MAX_USER_AGENT_LEN); } /* Resolve URL */ else if(CONF_VAR_IS(var, "RESOLVE_URL")) { if(options->resolve_url != NULL) free(options->resolve_url); tmpint = strlen(val)+1; options->resolve_url = malloc(tmpint); if(options->resolve_url == NULL) { fprintf(stderr, "Memory allocation error for resolve URL.\n"); exit(EXIT_FAILURE); } strlcpy(options->resolve_url, val, tmpint); } /* NAT Local ? */ else if(CONF_VAR_IS(var, "NAT_LOCAL")) { if(val[0] == 'y' || val[0] == 'Y') options->nat_local = 1; } /* NAT rand port ? */ else if(CONF_VAR_IS(var, "NAT_RAND_PORT")) { if(val[0] == 'y' || val[0] == 'Y') options->nat_rand_port = 1; } /* NAT port */ else if(CONF_VAR_IS(var, "NAT_PORT")) { tmpint = atoi(val); if(tmpint < 0 || tmpint > MAX_PORT) return(-1); else options->nat_port = tmpint; } return(0); }