fwknop/client/config_init.c
2009-08-09 15:43:16 +00:00

528 lines
18 KiB
C

/*
******************************************************************************
*
* File: config_init.c
*
* Author: Damien Stuart
*
* Purpose: Command-line and config file processing for fwknop client.
*
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
*
* License (GNU Public License):
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
******************************************************************************
*/
#include "fwknop_common.h"
#include "config_init.h"
#include "getopt.h"
#include "utils.h"
#include "ctype.h"
/* Routine to extract the configuration value from a line in the config
* file.
*/
int
get_char_val(const char *var_name, char *dest, char *lptr)
{
int i, var_char_ctr = 0;
char *tmp_ptr;
tmp_ptr = lptr;
/* var_name is guaranteed to be NULL-terminated.
*/
for (i=0; i < (int)strlen(var_name); i++)
if (tmp_ptr[i] != var_name[i])
return 0;
tmp_ptr += i;
/* First char after varName better be a space or tab or '='.
*/
if (*tmp_ptr != ' ' && *tmp_ptr != '\t' && *tmp_ptr != '=')
return 0;
/* Walk past the delimiter.
*/
while (*tmp_ptr == ' ' || *tmp_ptr == '\t' || *tmp_ptr == '=')
tmp_ptr++;
while (var_char_ctr < MAX_LINE_LEN && tmp_ptr[var_char_ctr] != '\n'
&& tmp_ptr[var_char_ctr] != '\0')
var_char_ctr++;
if (tmp_ptr[var_char_ctr] != '\n' || var_char_ctr >= MAX_LINE_LEN)
return 0;
strncpy(dest, tmp_ptr, var_char_ctr);
dest[var_char_ctr] = '\0';
return 1;
}
/* Parse any time offset from the command line
*/
static int
parse_time_offset(char *offset_str)
{
int i, j;
int offset = 0;
int offset_type = TIME_OFFSET_SECONDS;
int os_len = strlen(offset_str);
char offset_digits[MAX_TIME_STR_LEN];
j=0;
for (i=0; i < os_len; i++) {
if (isdigit(offset_str[i])) {
offset_digits[j] = offset_str[i];
j++;
} else if (offset_str[i] == 'm' || offset_str[i] == 'M') {
offset_type = TIME_OFFSET_MINUTES;
break;
} else if (offset_str[i] == 'h' || offset_str[i] == 'H') {
offset_type = TIME_OFFSET_HOURS;
break;
} else if (offset_str[i] == 'd' || offset_str[i] == 'D') {
offset_type = TIME_OFFSET_DAYS;
break;
}
}
offset_digits[j] = '\0';
if (j < 1) {
fprintf(stderr, "[*] Invalid time offset: %s", offset_str);
exit(EXIT_FAILURE);
}
offset = atoi(offset_digits);
if (offset < 0) {
fprintf(stderr, "[*] Invalid time offset: %s", offset_str);
exit(EXIT_FAILURE);
}
switch (offset_type) {
case TIME_OFFSET_MINUTES:
offset *= 60;
break;
case TIME_OFFSET_HOURS:
offset *= 60 * 60;
break;
case TIME_OFFSET_DAYS:
offset *= 60 * 60 * 24;
break;
}
return offset;
}
/* Parse the config file...
*/
static void
parse_config_file(fko_cli_options_t *options, struct opts_track* ot)
{
FILE *cfile_ptr;
unsigned int numLines = 0;
char conf_line_buf[MAX_LINE_LEN] = {0};
char tmp_char_buf[MAX_LINE_LEN] = {0};
char *lptr;
struct stat st;
/* First see if the config file exists. If it doesn't, and was
* specified via command-line, then error out. Otherwise, complain
* and go on with program defaults.
*/
if(stat(options->config_file, &st) != 0)
{
if(ot->got_config_file)
{
fprintf(stderr, "[*] Could not open config file: %s\n",
options->config_file);
exit(EXIT_FAILURE);
}
fprintf(stderr,
"** Config file was not found. Attempting to continue with defaults...\n"
);
return;
}
if ((cfile_ptr = fopen(options->config_file, "r")) == NULL)
{
fprintf(stderr, "[*] Could not open config file: %s\n",
options->config_file);
exit(EXIT_FAILURE);
}
while ((fgets(conf_line_buf, MAX_LINE_LEN, cfile_ptr)) != NULL)
{
numLines++;
conf_line_buf[MAX_LINE_LEN-1] = '\0';
lptr = conf_line_buf;
memset(tmp_char_buf, 0x0, MAX_LINE_LEN);
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
lptr++;
/* Get past comments and empty lines.
*/
if (*lptr == '#' || *lptr == '\n' || *lptr == '\r' || *lptr == '\0' || *lptr == ';')
continue;
/*--DSS TODO: Figure out what to put here
if (ot->got_device == 0 || options->interface.name[0] == '\0')
get_char_val("XXXX", options->interface.name, lptr);
if (ot->got_snaplen == 0 && get_char_val("SNAPLEN", tmp_char_buf, lptr))
options->snapLen = atoi(tmp_char_buf);
*/
}
fclose(cfile_ptr);
return;
}
/* Sanity and bounds checks for the various options.
*/
static void
validate_options(fko_cli_options_t *options)
{
/* Gotta have a Destination unless we are just testing or getting the
* the version, and must use one of [-s|-R|-a].
*/
if(!options->test && !options->version && !options->show_last_command)
{
if (options->spa_server_str[0] == 0x0)
{
fprintf(stderr,
"[*] Must use --destination unless --test mode is used\n");
exit(EXIT_FAILURE);
}
if (!options->resolve_ip_http && options->allow_ip_str[0] == 0x0)
{
fprintf(stderr,
"[*] Must use one of [-s|-R|-a] to specify IP for SPA access.\n");
exit(EXIT_FAILURE);
}
}
if(options->resolve_ip_http || options->spa_proto == FKO_PROTO_HTTP)
if (options->http_user_agent[0] == '\0')
snprintf(options->http_user_agent, HTTP_MAX_USER_AGENT_LEN,
"%s%s", "Fwknop/", MY_VERSION);
/* If we are using gpg, we must at least have the recipient set.
*/
if(options->use_gpg)
{
if(options->gpg_recipient_key == NULL
|| strlen(options->gpg_recipient_key) == 0)
{
fprintf(stderr,
"[*] Must specify --gpg-recipient-key when GPG is used.\n");
exit(EXIT_FAILURE);
}
}
return;
}
/* Initialize program configuration via config file and/or command-line
* switches.
*/
void
config_init(fko_cli_options_t *options, int argc, char **argv)
{
int cmd_arg, index;
struct opts_track ot;
/* Zero out options and opts_track.
*/
memset(options, 0x00, sizeof(fko_cli_options_t));
memset(&ot, 0x00, sizeof(ot));
/* Establish a few defaults such as UDP/62201 for sending the SPA
* packet (can be changed with --server-proto/--server-port)
*/
options->spa_proto = FKO_DEFAULT_PROTO;
options->spa_dst_port = FKO_DEFAULT_PORT;
options->fw_timeout = -1;
while ((cmd_arg = getopt_long(argc, argv,
"a:A:bB:C:D:f:gG:hIm:nN:p:P:qQ:rRsS:Tu:U:vV", cmd_opts, &index)) != -1) {
switch(cmd_arg) {
case 'a':
strlcpy(options->allow_ip_str, optarg, MAX_IP_STR_LEN);
break;
case 'A':
strlcpy(options->access_str, optarg, MAX_LINE_LEN);
break;
case 'b':
options->save_packet_file_append = 1;
break;
case 'B':
strlcpy(options->save_packet_file, optarg, MAX_PATH_LEN);
break;
case 'C':
strlcpy(options->server_command, optarg, MAX_LINE_LEN);
break;
case 'D':
strlcpy(options->spa_server_str, optarg, MAX_SERVER_STR_LEN);
break;
case 'f':
options->fw_timeout = atoi(optarg);
if (options->fw_timeout < 0) {
fprintf(stderr, "[*] --fw-timeout must be >= 0\n");
exit(EXIT_FAILURE);
}
break;
case 'g':
case GPG_ENCRYPTION:
options->use_gpg = 1;
break;
case 'G':
strlcpy(options->get_key_file, optarg, MAX_PATH_LEN);
break;
case 'h':
usage();
exit(EXIT_SUCCESS);
case 'm':
case FKO_DIGEST_NAME:
if(strncasecmp(optarg, "md5", 3) == 0)
options->digest_type = FKO_DIGEST_MD5;
else if(strncasecmp(optarg, "sha1", 4) == 0)
options->digest_type = FKO_DIGEST_SHA1;
else if(strncasecmp(optarg, "sha256", 6) == 0)
options->digest_type = FKO_DIGEST_SHA256;
else if(strncasecmp(optarg, "sha384", 6) == 0)
options->digest_type = FKO_DIGEST_SHA384;
else if(strncasecmp(optarg, "sha512", 6) == 0)
options->digest_type = FKO_DIGEST_SHA512;
else
{
fprintf(stderr, "* Invalid digest type: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'n':
options->no_save = 1;
break;
case 'N':
strlcpy(options->nat_access_str, optarg, MAX_LINE_LEN);
break;
case 'p':
options->spa_dst_port = atoi(optarg);
if (options->spa_dst_port < 0 || options->spa_dst_port > 65535) {
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'P':
if (strncmp(optarg, "udp", strlen("udp")) == 0)
options->spa_proto = FKO_PROTO_UDP;
else if (strncmp(optarg, "tcpraw", strlen("tcpraw")) == 0)
options->spa_proto = FKO_PROTO_TCP_RAW;
else if (strncmp(optarg, "tcp", strlen("tcp")) == 0)
options->spa_proto = FKO_PROTO_TCP;
else if (strncmp(optarg, "icmp", strlen("icmp")) == 0)
options->spa_proto = FKO_PROTO_ICMP;
else if (strncmp(optarg, "http", strlen("http")) == 0)
options->spa_proto = FKO_PROTO_HTTP;
else {
fprintf(stderr, "[*] Unrecognized protocol: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'q':
options->quiet = 1;
break;
case 'Q':
strlcpy(options->spoof_ip_src_str, optarg, MAX_IP_STR_LEN);
break;
case 'r':
options->rand_port = 1;
break;
case 'R':
options->resolve_ip_http = 1;
break;
case SHOW_LAST_ARGS:
options->show_last_command = 1;
break;
case 's':
strlcpy(options->allow_ip_str, "0.0.0.0", MAX_IP_STR_LEN);
break;
case 'S':
options->spa_src_port = atoi(optarg);
if (options->spa_src_port < 0 || options->spa_src_port > 65535) {
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case 'T':
options->test = 1;
break;
case 'u':
strlcpy(options->http_user_agent, optarg, HTTP_MAX_USER_AGENT_LEN);
break;
case 'U':
strlcpy(options->spoof_user, optarg, MAX_USERNAME_LEN);
break;
case 'v':
options->verbose = 1;
break;
case 'V':
options->version = 1;
break;
case GPG_RECIP_KEY:
options->use_gpg = 1;
strlcpy(options->gpg_recipient_key, optarg, MAX_GPG_KEY_ID);
break;
case GPG_SIGNER_KEY:
options->use_gpg = 1;
strlcpy(options->gpg_signer_key, optarg, MAX_GPG_KEY_ID);
break;
case GPG_HOME_DIR:
options->use_gpg = 1;
strlcpy(options->gpg_home_dir, optarg, MAX_PATH_LEN);
break;
case GPG_AGENT:
options->use_gpg = 1;
options->use_gpg_agent = 1;
break;
case NAT_LOCAL:
options->nat_local = 1;
break;
case NAT_RAND_PORT:
options->nat_rand_port = 1;
break;
case NAT_PORT:
options->nat_port = atoi(optarg);
if (options->nat_port < 0 || options->nat_port > 65535) {
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
exit(EXIT_FAILURE);
}
break;
case TIME_OFFSET_PLUS:
options->time_offset_plus = parse_time_offset(optarg);
break;
case TIME_OFFSET_MINUS:
options->time_offset_minus = parse_time_offset(optarg);
break;
case NO_SAVE_ARGS:
options->no_save_args = 1;
break;
default:
usage();
exit(EXIT_FAILURE);
}
}
/* Parse configuration file to populate any params not already specified
* via command-line options
*/
//--DSS XXX: We will use this when we have a config file to use.
//parse_config_file(options, &ot);
/* Now that we have all of our options set, we can validate them.
*/
validate_options(options);
return;
}
/* Print usage message...
*/
void
usage(void)
{
fprintf(stderr, "\n%s client version %s\n%s\n\n", MY_NAME, MY_VERSION, MY_DESC);
fprintf(stderr,
"Usage: fwknop -A <port list> [-s|-R|-a] -D <spa_server> [options]\n\n"
" -h, --help - Print this usage message and exit.\n"
" -c, --config-file - Specify an alternate configuration file.\n"
" -A, --access - Provide a list of ports/protocols to open\n"
" on the server.\n"
" -B, --save-packet - Save the generated packet data to the\n"
" specified file.\n"
" -a, --allow-ip - Specify IP address to allow within the SPA\n"
" packet.\n"
" -D, --destination - Specify the IP address of the fwknop server.\n"
" -N, --nat-access - Gain NAT access to an internal service\n"
" protected by the fwknop server.\n"
" -p, --server-port - Set the destination port for outgoing SPA\n"
" packet.\n"
" -P, --server-proto - Set the protocol (udp, tcp, tcpraw, icmp) for\n"
" the outgoing SPA packet. Note: The 'tcpraw'\n"
" and 'icmp' modes use raw sockets and thus\n"
" require root access to run.\n"
" -s, --source-ip - Tell the fwknopd server to accept whatever\n"
" source IP the SPA packet has as the IP that\n"
" needs access (not recommended, and the\n"
" fwknopd server can ignore such requests).\n"
" -S, --source-port - Set the source port for outgoing SPA packet.\n"
" -Q, --spoof-source - Set the source IP for outgoing SPA packet.\n"
" -R, --resolve-ip-http - Resolve the external network IP by\n"
" connecting to the URL:\n"
" http://"
HTTP_RESOLVE_HOST
HTTP_RESOLVE_URL
"\n"
" -u, --user-agent - Set the HTTP User-Agent for resolving the\n"
" external IP via -R, or for sending SPA\n"
" packets over HTTP.\n"
" -U, --spoof-user - Set the username within outgoing SPA packet.\n"
" -q, --quiet - Perform fwknop functions quietly.\n"
" -G, --get-key - Load an encryption key/password from a file.\n"
" -r, --rand-port - Send the SPA packet over a randomly assigned\n"
" port (requires a broader pcap filter on the\n"
" server side than the default of udp 62201).\n"
" -T, --test - Build the SPA packet but do not send it over\n"
" the network.\n"
" -v, --verbose - Set verbose mode.\n"
" -V, --version - Print version number.\n"
" -m, --digest-type - Speciy the message digest algorithm to use.\n"
" (md5, sha1, or sha256 (default)).\n"
" -f, --fw-timeout - Specify SPA server firewall timeout from the\n"
" client side.\n"
" --gpg-encryption - Use GPG encyrption (default is Rijndael).\n"
" --gpg-recipient-key - Specify the recipient GPG key name or ID.\n"
" --gpg-signer-key - Specify the signer's GPG key name or ID.\n"
" --gpg-home-dir - Specify the GPG home directory.\n"
" --gpg-agent - Use GPG agent if available.\n"
" --nat-local - Access a local service via a forwarded port\n"
" on the fwknopd server system.\n"
" --nat-port - Specify the port to forward to access a\n"
" service via NAT.\n"
" --nat-rand-port - Have the fwknop client assign a random port\n"
" for NAT access.\n"
" --show-last - Show the last fwknop command line arguments.\n"
" --time-offset-plus - Add time to outgoing SPA packet timestamp.\n"
" --time-offset-minus - Subtract time from outgoing SPA packet\n"
" timestamp.\n"
"\n"
);
return;
}
/***EOF***/