2417 lines
86 KiB
C
2417 lines
86 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* \file config_init.c
|
|
*
|
|
* \brief Command-line and config file processing for fwknop client.
|
|
*
|
|
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
|
|
* Copyright (C) 2009-2014 fwknop developers and contributors. For a full
|
|
* list of contributors, see the file 'CREDITS'.
|
|
*
|
|
* License (GNU General Public License):
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* 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 "netinet_common.h"
|
|
#include "config_init.h"
|
|
#include "cmd_opts.h"
|
|
#include "utils.h"
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef WIN32
|
|
#define STDIN_FILENO 0
|
|
#endif
|
|
|
|
#define RC_PARAM_TEMPLATE "%-24s %s\n" /*!< Template to define param = val in a rc file */
|
|
#define RC_SECTION_DEFAULT "default" /*!< Name of the default section in fwknoprc */
|
|
#define RC_SECTION_TEMPLATE "[%s]\n" /*!< Template to define a section in a rc file */
|
|
#define FWKNOPRC_OFLAGS (O_WRONLY|O_CREAT|O_EXCL) /*!< O_flags used to create an fwknoprc file with the open function */
|
|
#define FWKNOPRC_MODE (S_IRUSR|S_IWUSR) /*!< mode used to create an fwknoprc file with the open function */
|
|
#define PARAM_YES_VALUE "Y" /*!< String which represents a YES value for a parameter in fwknoprc */
|
|
#define PARAM_NO_VALUE "N" /*!< String which represents a NO value for a parameter in fwknoprc */
|
|
#define POSITION_TO_BITMASK(x) ((uint32_t)(1) << ((x) % 32)) /*!< Macro do get a bitmask from a position */
|
|
#define BITMASK_ARRAY_SIZE 2 /*!< Number of 32bits integer used to handle bitmask in the fko_var_bitmask_t structure */
|
|
#define LF_CHAR 0x0A /*!< Hexadecimal value associated to the LF char */
|
|
|
|
/**
|
|
* Structure to handle long bitmask.
|
|
*
|
|
* The structure is built as an array of unsigned 32 bits integer to be able to
|
|
* easily increase the size of the bitmask.
|
|
* This bitmask can contains at most (BITMASK_ARRAY_SIZE * 32) values.
|
|
*/
|
|
typedef struct fko_var_bitmask
|
|
{
|
|
uint32_t dw[BITMASK_ARRAY_SIZE]; /*!< Array of bitmasks */
|
|
} fko_var_bitmask_t;
|
|
|
|
/**
|
|
* Structure to handle a variable in an rcfile (name and value)
|
|
*/
|
|
typedef struct rc_file_param
|
|
{
|
|
char name[MAX_LINE_LEN]; /*!< Variable name */
|
|
char val[MAX_LINE_LEN]; /*!< Variable value */
|
|
} rc_file_param_t;
|
|
|
|
/**
|
|
* Structure to identify a configuration variable (name and position)
|
|
*/
|
|
typedef struct fko_var
|
|
{
|
|
const char name[32]; /*!< Variable name in fwknoprc */
|
|
unsigned int pos; /*!< Variable position from the fwknop_cli_arg_t enumeration */
|
|
} fko_var_t;
|
|
|
|
enum
|
|
{
|
|
FWKNOP_CLI_FIRST_ARG = 0,
|
|
FWKNOP_CLI_ARG_DIGEST_TYPE = 0,
|
|
FWKNOP_CLI_ARG_SPA_SERVER_PROTO,
|
|
FWKNOP_CLI_ARG_SPA_SERVER_PORT,
|
|
FWKNOP_CLI_ARG_SPA_SOURCE_PORT,
|
|
FWKNOP_CLI_ARG_FW_TIMEOUT,
|
|
FWKNOP_CLI_ARG_ALLOW_IP,
|
|
FWKNOP_CLI_ARG_TIME_OFFSET,
|
|
FWKNOP_CLI_ARG_ENCRYPTION_MODE,
|
|
FWKNOP_CLI_ARG_USE_GPG,
|
|
FWKNOP_CLI_ARG_USE_GPG_AGENT,
|
|
FWKNOP_CLI_ARG_GPG_NO_SIGNING_PW,
|
|
FWKNOP_CLI_ARG_GPG_RECIPIENT,
|
|
FWKNOP_CLI_ARG_GPG_SIGNER,
|
|
FWKNOP_CLI_ARG_GPG_HOMEDIR,
|
|
FWKNOP_CLI_ARG_SPOOF_USER,
|
|
FWKNOP_CLI_ARG_SPOOF_SOURCE_IP,
|
|
FWKNOP_CLI_ARG_ACCESS,
|
|
FWKNOP_CLI_ARG_SPA_SERVER,
|
|
FWKNOP_CLI_ARG_RAND_PORT,
|
|
FWKNOP_CLI_ARG_KEY_RIJNDAEL,
|
|
FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64,
|
|
FWKNOP_CLI_ARG_GPG_SIGNING_PW,
|
|
FWKNOP_CLI_ARG_GPG_SIGNING_PW_BASE64,
|
|
FWKNOP_CLI_ARG_HMAC_DIGEST_TYPE,
|
|
FWKNOP_CLI_ARG_KEY_HMAC_BASE64,
|
|
FWKNOP_CLI_ARG_KEY_HMAC,
|
|
FWKNOP_CLI_ARG_USE_HMAC,
|
|
FWKNOP_CLI_ARG_KEY_FILE,
|
|
FWKNOP_CLI_ARG_HMAC_KEY_FILE,
|
|
FWKNOP_CLI_ARG_NAT_ACCESS,
|
|
FWKNOP_CLI_ARG_HTTP_USER_AGENT,
|
|
FWKNOP_CLI_ARG_RESOLVE_URL,
|
|
FWKNOP_CLI_ARG_NAT_LOCAL,
|
|
FWKNOP_CLI_ARG_NAT_RAND_PORT,
|
|
FWKNOP_CLI_ARG_NAT_PORT,
|
|
FWKNOP_CLI_ARG_VERBOSE,
|
|
FWKNOP_CLI_ARG_RESOLVE_IP_HTTP,
|
|
FWKNOP_CLI_LAST_ARG
|
|
} fwknop_cli_arg_t;
|
|
|
|
static fko_var_t fko_var_array[FWKNOP_CLI_LAST_ARG] =
|
|
{
|
|
{ "DIGEST_TYPE", FWKNOP_CLI_ARG_DIGEST_TYPE },
|
|
{ "SPA_SERVER_PROTO", FWKNOP_CLI_ARG_SPA_SERVER_PROTO },
|
|
{ "SPA_SERVER_PORT", FWKNOP_CLI_ARG_SPA_SERVER_PORT },
|
|
{ "SPA_SOURCE_PORT", FWKNOP_CLI_ARG_SPA_SOURCE_PORT },
|
|
{ "FW_TIMEOUT", FWKNOP_CLI_ARG_FW_TIMEOUT },
|
|
{ "ALLOW_IP", FWKNOP_CLI_ARG_ALLOW_IP },
|
|
{ "TIME_OFFSET", FWKNOP_CLI_ARG_TIME_OFFSET },
|
|
{ "ENCRYPTION_MODE", FWKNOP_CLI_ARG_ENCRYPTION_MODE },
|
|
{ "USE_GPG", FWKNOP_CLI_ARG_USE_GPG },
|
|
{ "USE_GPG_AGENT", FWKNOP_CLI_ARG_USE_GPG_AGENT },
|
|
{ "GPG_RECIPIENT", FWKNOP_CLI_ARG_GPG_RECIPIENT },
|
|
{ "GPG_SIGNER", FWKNOP_CLI_ARG_GPG_SIGNER },
|
|
{ "GPG_HOMEDIR", FWKNOP_CLI_ARG_GPG_HOMEDIR },
|
|
{ "GPG_SIGNING_PW", FWKNOP_CLI_ARG_GPG_SIGNING_PW },
|
|
{ "GPG_SIGNING_PW_BASE64", FWKNOP_CLI_ARG_GPG_SIGNING_PW_BASE64 },
|
|
{ "GPG_NO_SIGNING_PW", FWKNOP_CLI_ARG_GPG_NO_SIGNING_PW },
|
|
{ "SPOOF_USER", FWKNOP_CLI_ARG_SPOOF_USER },
|
|
{ "SPOOF_SOURCE_IP", FWKNOP_CLI_ARG_SPOOF_SOURCE_IP },
|
|
{ "ACCESS", FWKNOP_CLI_ARG_ACCESS },
|
|
{ "SPA_SERVER", FWKNOP_CLI_ARG_SPA_SERVER },
|
|
{ "RAND_PORT", FWKNOP_CLI_ARG_RAND_PORT },
|
|
{ "KEY", FWKNOP_CLI_ARG_KEY_RIJNDAEL },
|
|
{ "KEY_BASE64", FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64 },
|
|
{ "HMAC_DIGEST_TYPE", FWKNOP_CLI_ARG_HMAC_DIGEST_TYPE },
|
|
{ "HMAC_KEY_BASE64", FWKNOP_CLI_ARG_KEY_HMAC_BASE64 },
|
|
{ "HMAC_KEY", FWKNOP_CLI_ARG_KEY_HMAC },
|
|
{ "USE_HMAC", FWKNOP_CLI_ARG_USE_HMAC },
|
|
{ "KEY_FILE", FWKNOP_CLI_ARG_KEY_FILE },
|
|
{ "HMAC_KEY_FILE", FWKNOP_CLI_ARG_HMAC_KEY_FILE },
|
|
{ "NAT_ACCESS", FWKNOP_CLI_ARG_NAT_ACCESS },
|
|
{ "HTTP_USER_AGENT", FWKNOP_CLI_ARG_HTTP_USER_AGENT },
|
|
{ "RESOLVE_URL", FWKNOP_CLI_ARG_RESOLVE_URL },
|
|
{ "NAT_LOCAL", FWKNOP_CLI_ARG_NAT_LOCAL },
|
|
{ "NAT_RAND_PORT", FWKNOP_CLI_ARG_NAT_RAND_PORT },
|
|
{ "NAT_PORT", FWKNOP_CLI_ARG_NAT_PORT },
|
|
{ "VERBOSE", FWKNOP_CLI_ARG_VERBOSE },
|
|
{ "RESOLVE_IP_HTTP", FWKNOP_CLI_ARG_RESOLVE_IP_HTTP }
|
|
};
|
|
|
|
/* Array to define which conf. variables are critical and should not be
|
|
* overwritten when a stanza is updated using the --save-rc-stanza arg
|
|
* without the user validation */
|
|
static int critical_var_array[] =
|
|
{
|
|
FWKNOP_CLI_ARG_KEY_RIJNDAEL,
|
|
FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64,
|
|
FWKNOP_CLI_ARG_KEY_HMAC,
|
|
FWKNOP_CLI_ARG_KEY_HMAC_BASE64,
|
|
FWKNOP_CLI_ARG_GPG_RECIPIENT,
|
|
FWKNOP_CLI_ARG_GPG_SIGNER,
|
|
FWKNOP_CLI_ARG_GPG_SIGNING_PW,
|
|
FWKNOP_CLI_ARG_GPG_SIGNING_PW_BASE64
|
|
};
|
|
|
|
/**
|
|
* @brief Generate Rijndael + HMAC keys from /dev/random (base64 encoded) and exit.
|
|
*
|
|
* @param options FKO command line option structure
|
|
*/
|
|
static void
|
|
generate_keys(fko_cli_options_t *options)
|
|
{
|
|
int res;
|
|
|
|
/* If asked, we have to generate the keys */
|
|
if(options->key_gen)
|
|
{
|
|
/* Zero out the key buffers */
|
|
memset(&(options->key_base64), 0x00, sizeof(options->key_base64));
|
|
memset(&(options->hmac_key_base64), 0x00, sizeof(options->hmac_key_base64));
|
|
|
|
/* Generate the key with through libfko */
|
|
res = fko_key_gen(options->key_base64, options->key_len,
|
|
options->hmac_key_base64, options->hmac_key_len,
|
|
options->hmac_type);
|
|
|
|
/* Exit upon key generation failure*/
|
|
if(res != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "%s: fko_key_gen: Error %i - %s",
|
|
MY_NAME, res, fko_errstr(res));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Everything is ok - nothing to do */
|
|
else;
|
|
}
|
|
|
|
/* No key generation asked - nothing to do */
|
|
else;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if a variable is a critical var.
|
|
*
|
|
* This function check the critical_var_array table to find if the variable
|
|
* position is available.
|
|
*
|
|
* @param var_pos Fwknop configuration variable position
|
|
*
|
|
* @return 1 the variable is critical, 0 otherwise
|
|
*/
|
|
static int
|
|
var_is_critical(short var_pos)
|
|
{
|
|
int ndx; /* Index on the critical_var_array array */
|
|
int var_found = 0;
|
|
|
|
/* Go through the array of critical vars */
|
|
for (ndx=0 ; ndx<ARRAY_SIZE(critical_var_array) ; ndx++)
|
|
{
|
|
/* and check if we find it */
|
|
if (var_pos == critical_var_array[ndx])
|
|
{
|
|
var_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return var_found;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a variable to a bitmask
|
|
*
|
|
* This function adds the bitmask associated to a variable position, to a
|
|
* bitmask.
|
|
*
|
|
* @param var_pos Fwknop configuration variable position
|
|
* @param bm fko_var_bitmask_t variable to update
|
|
*/
|
|
static void
|
|
add_var_to_bitmask(short var_pos, fko_var_bitmask_t *bm)
|
|
{
|
|
unsigned int bitmask_ndx;
|
|
|
|
/* Look for the index on the uint32_t array we have to process */
|
|
bitmask_ndx = var_pos / 32;
|
|
|
|
/* Set the bitmask according to the index found */
|
|
if (bitmask_ndx < BITMASK_ARRAY_SIZE)
|
|
bm->dw[bitmask_ndx] |= POSITION_TO_BITMASK(var_pos);
|
|
|
|
/* The index on the uint32_t bitmask is invalid */
|
|
else
|
|
log_msg(LOG_VERBOSITY_WARNING, "add_var_to_bitmask() : Bad variable position %u", var_pos);
|
|
}
|
|
|
|
/**
|
|
* @brief Remove a variable from a bitmask
|
|
*
|
|
* This function removes the bitmask associated to the variable position from a
|
|
* bitmask.
|
|
*
|
|
* @param var_pos Fwknop configuration variable position
|
|
* @param bm fko_var_bitmask_t structure to update
|
|
*/
|
|
static void
|
|
remove_var_from_bitmask(short var_pos, fko_var_bitmask_t *bm)
|
|
{
|
|
unsigned int bitmask_ndx;
|
|
|
|
/* Look for the index on the uint32_t array we have to process */
|
|
bitmask_ndx = var_pos / 32;
|
|
|
|
/* Set the bitmask according to the index found */
|
|
if (bitmask_ndx < BITMASK_ARRAY_SIZE)
|
|
bm->dw[bitmask_ndx] &= ~POSITION_TO_BITMASK(var_pos);
|
|
|
|
/* The index on the uint32_t bitmask is invalid */
|
|
else
|
|
log_msg(LOG_VERBOSITY_WARNING, "remove_from_bitmask() : Bad variable position %u", var_pos);
|
|
}
|
|
|
|
/**
|
|
* @brief Return whether a variable is available in a bitmask
|
|
*
|
|
* The variable bitmask is looked for in the bitmask.
|
|
*
|
|
* @param var_pos Fwknop configuration variable position
|
|
* @param bm fko_var_bitmask_t structure to check
|
|
*
|
|
* @return 1 if the bitmsk contains the variable, 0 otherwise.
|
|
*/
|
|
static int
|
|
bitmask_has_var(short var_pos, fko_var_bitmask_t *bm)
|
|
{
|
|
unsigned int bitmask_ndx;
|
|
int var_found = 0;
|
|
|
|
/* Look for the index on the uint32_t array we have to process */
|
|
bitmask_ndx = var_pos / 32;
|
|
|
|
/* Check the bitmask according to the index found */
|
|
if (bitmask_ndx < BITMASK_ARRAY_SIZE)
|
|
{
|
|
if ( bm->dw[bitmask_ndx] & POSITION_TO_BITMASK(var_pos) )
|
|
var_found = 1;
|
|
}
|
|
|
|
/* The index on the uint32_t bitmask is invalid */
|
|
else
|
|
log_msg(LOG_VERBOSITY_WARNING, "bitmask_has_var_ndx() : Bad variable position %u", var_pos);
|
|
|
|
return var_found;
|
|
}
|
|
|
|
/**
|
|
* @brief Ask the user if a variable must be overwritten or not for a specific stanza
|
|
*
|
|
* If the user sets other chars than a 'y' char, we assume he does not want to
|
|
* overwrite the variable.
|
|
*
|
|
* @param var Variable which should be overwritten
|
|
* @param stanza Stanza where the variable should be overwritten
|
|
*
|
|
* @return 1 if the user wants to overwrite the variable, 0 otherwise
|
|
*/
|
|
static int
|
|
ask_overwrite_var(const char *var, const char *stanza)
|
|
{
|
|
char user_input = 'N';
|
|
int overwrite = 0;
|
|
int c;
|
|
int first_char = 1;;
|
|
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"Variable '%s' found in stanza '%s'. Overwrite [N/y] ? ",
|
|
var, stanza);
|
|
|
|
while ((c=getchar()) != LF_CHAR)
|
|
{
|
|
if (first_char)
|
|
user_input = c;
|
|
first_char = 0;
|
|
}
|
|
|
|
if (user_input == 'y')
|
|
overwrite = 1;
|
|
|
|
return overwrite;
|
|
}
|
|
|
|
/**
|
|
* @brief Lookup a variable in the variable array according to its name
|
|
*
|
|
* This function parses the fko_var_array table and try to find a match
|
|
* for the user string, which indicates we have found a configuration variable.
|
|
*
|
|
* @param str String to compare against every fwknop conf variables
|
|
*
|
|
* @return A pointer on the variable structure, or NULL if not found
|
|
*/
|
|
static fko_var_t *
|
|
lookup_var_by_name(const char *var_name)
|
|
{
|
|
short ndx; /* Index on the the fko_var_array table */
|
|
fko_var_t *var = NULL;
|
|
|
|
/* Check str against each variable available in fko_var_array */
|
|
for (ndx=0 ; ndx<ARRAY_SIZE(fko_var_array) ; ndx++)
|
|
{
|
|
if (CONF_VAR_IS(var_name, fko_var_array[ndx].name))
|
|
{
|
|
var = &(fko_var_array[ndx]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
/**
|
|
* @brief Lookup a variable in the variable array according to its position
|
|
*
|
|
* This function parses the fko_var_array table and try to find a match
|
|
* for the position, which indicates we have found a configuration variable.
|
|
*
|
|
* @param var_pos Position to compare against every fwknop conf variables
|
|
*
|
|
* @return A pointer on the variable structure, or NULL if not found
|
|
*/
|
|
static fko_var_t *
|
|
lookup_var_by_position(short var_pos)
|
|
{
|
|
short ndx; /* Index on the the fko_var_array table */
|
|
fko_var_t *var = NULL;
|
|
|
|
/* Check str against each variable available in fko_var_array */
|
|
for (ndx=0 ; ndx<ARRAY_SIZE(fko_var_array) ; ndx++)
|
|
{
|
|
if (var_pos == fko_var_array[ndx].pos)
|
|
{
|
|
var = &(fko_var_array[ndx]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
/**
|
|
* @brief Set a string as a Yes or No value according to a boolean (0 or 1).
|
|
*
|
|
* This function checks whether a value is set to zero or not, and updates a
|
|
* string to a YES_NO parameter value.
|
|
* The string must be zeroed before being passed to the function.
|
|
*
|
|
* @param val Variable to check
|
|
* @param s String where to store the YES_NO value.
|
|
* @param len Number of bytes avaialble for the s buffer.
|
|
*/
|
|
static void
|
|
bool_to_yesno(int val, char* s, size_t len)
|
|
{
|
|
if (val == 0)
|
|
strlcpy(s, PARAM_NO_VALUE, len);
|
|
else
|
|
strlcpy(s, PARAM_YES_VALUE, len);
|
|
}
|
|
|
|
/**
|
|
* @brief Is a string formatted as YES string.
|
|
*
|
|
* @param s String to check for a YES string
|
|
*
|
|
* @return 1 if the string match the YES pattern, 0 otherwise
|
|
*/
|
|
static int
|
|
is_yes_str(const char *s)
|
|
{
|
|
int valid;
|
|
|
|
if (strcasecmp(PARAM_YES_VALUE, s) == 0)
|
|
valid = 1;
|
|
else
|
|
valid = 0;
|
|
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if a section is in a line and fetch it.
|
|
*
|
|
* This function parses a NULL terminated string in order to find a section,
|
|
* something like [mysection]. If it succeeds, the stanza is retrieved.
|
|
*
|
|
* @param line String containing a line from the rc file to check for a section
|
|
* @param line_size size of the line buffer
|
|
* @param rc_section String to store the section found
|
|
* @param rc_section_size Size of the rc_section buffer
|
|
*
|
|
* @return 1 if a section was found, 0 otherwise
|
|
*/
|
|
static int
|
|
is_rc_section(const char* line, uint16_t line_size, char* rc_section, uint16_t rc_section_size)
|
|
{
|
|
char *ndx, *emark;
|
|
char buf[MAX_LINE_LEN] = {0};
|
|
int section_found = 0;
|
|
|
|
if (line_size < sizeof(buf))
|
|
{
|
|
strlcpy(buf, line, sizeof(buf));
|
|
|
|
ndx = buf;
|
|
|
|
while(isspace(*ndx))
|
|
ndx++;
|
|
|
|
if(*ndx == '[')
|
|
{
|
|
ndx++;
|
|
emark = strchr(ndx, ']');
|
|
if(emark != NULL)
|
|
{
|
|
*emark = '\0';
|
|
memset(rc_section, 0, rc_section_size);
|
|
strlcpy(rc_section, ndx, rc_section_size);
|
|
section_found = 1;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
return section_found;
|
|
}
|
|
|
|
/**
|
|
* @brief Grab a variable and its value from a rc line.
|
|
*
|
|
* @param line Line to parse for a variable
|
|
* @param param Parameter structure where to store the variable name and its value
|
|
*
|
|
* @return 0 if no variable has been found, 1 otherwise.
|
|
*/
|
|
static int
|
|
is_rc_param(const char *line, rc_file_param_t *param)
|
|
{
|
|
char var[MAX_LINE_LEN] = {0};
|
|
char val[MAX_LINE_LEN] = {0};
|
|
char *ndx;
|
|
|
|
memset(param, 0, sizeof(*param));
|
|
|
|
/* Fetch the variable and its value */
|
|
if(sscanf(line, "%s %[^ ;\t\n\r#]", var, val) != 2)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"*Invalid entry in '%s'", line);
|
|
return 0;
|
|
}
|
|
|
|
/* Remove any colon that may be on the end of the var */
|
|
if((ndx = strrchr(var, ':')) != NULL)
|
|
*ndx = '\0';
|
|
|
|
/* Even though sscanf should automatically add a terminating
|
|
* NULL byte, an assumption is made that the input arrays are
|
|
* big enough, so we'll force a terminating NULL byte regardless
|
|
*/
|
|
var[MAX_LINE_LEN-1] = 0x0;
|
|
val[MAX_LINE_LEN-1] = 0x0;
|
|
|
|
/* Copy back the val and var in the structure */
|
|
strlcpy(param->name, var, sizeof(param->name));
|
|
strlcpy(param->val, val, sizeof(param->val));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Dump available stanzas from a fwknoprc file
|
|
*
|
|
* This function parses a rcfile and looks for configured stanzas.
|
|
* They are all displayed except the default stanza.
|
|
*
|
|
* @param rcfile full path to the rcfile to parse
|
|
*/
|
|
static void
|
|
dump_configured_stanzas_from_rcfile(const char* rcfile)
|
|
{
|
|
FILE *rc;
|
|
char line[MAX_LINE_LEN] = {0};
|
|
char curr_stanza[MAX_LINE_LEN] = {0};
|
|
|
|
/* Open the rcfile in read mode */
|
|
if ((rc = fopen(rcfile, "r")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING, "Unable to open rc file: %s: %s",
|
|
rcfile, strerror(errno));
|
|
|
|
return;
|
|
}
|
|
|
|
log_msg(LOG_VERBOSITY_NORMAL, "The following stanzas are configured in %s :", rcfile);
|
|
|
|
/* Parse the rcfile line by line to find stanza */
|
|
while ((fgets(line, MAX_LINE_LEN, rc)) != NULL)
|
|
{
|
|
line[MAX_LINE_LEN-1] = '\0';
|
|
|
|
/* Get past comments and empty lines (note: we only look at the first
|
|
* character. */
|
|
if(IS_EMPTY_LINE(line[0]))
|
|
continue;
|
|
|
|
/* Check which section we are working on */
|
|
else if (is_rc_section(line, strlen(line), curr_stanza, sizeof(curr_stanza)))
|
|
{
|
|
/* Print the stanza and continue - we exclude the default stanza */
|
|
if (strcasecmp(curr_stanza, RC_SECTION_DEFAULT) != 0)
|
|
log_msg(LOG_VERBOSITY_NORMAL, " - %s", curr_stanza);
|
|
continue;
|
|
}
|
|
|
|
/* Nothing we care about */
|
|
else;
|
|
}
|
|
|
|
fclose(rc);
|
|
}
|
|
|
|
/* Assign path to fwknop rc file
|
|
*/
|
|
static void
|
|
set_rc_file(char *rcfile, fko_cli_options_t *options)
|
|
{
|
|
int rcf_offset;
|
|
char *homedir;
|
|
|
|
memset(rcfile, 0x0, MAX_PATH_LEN);
|
|
|
|
if(options->rc_file[0] == 0x0)
|
|
{
|
|
#ifdef WIN32
|
|
homedir = getenv("USERPROFILE");
|
|
#else
|
|
homedir = getenv("HOME");
|
|
#endif
|
|
|
|
if(homedir == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Warning: Unable to determine HOME directory.\n"
|
|
" No .fwknoprc file processed.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strlcpy(rcfile, homedir, MAX_PATH_LEN);
|
|
|
|
rcf_offset = strlen(rcfile);
|
|
|
|
/* Sanity check the path to .fwknoprc.
|
|
* The preceeding path plus the path separator and '.fwknoprc' = 11
|
|
* cannot exceed MAX_PATH_LEN.
|
|
*/
|
|
if(rcf_offset > (MAX_PATH_LEN - 11))
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Warning: Path to .fwknoprc file is too long.\n"
|
|
" No .fwknoprc file processed.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
rcfile[rcf_offset] = PATH_SEP;
|
|
strlcat(rcfile, ".fwknoprc", MAX_PATH_LEN);
|
|
}
|
|
else
|
|
{
|
|
strlcpy(rcfile, options->rc_file, MAX_PATH_LEN);
|
|
}
|
|
|
|
/* Check rc file permissions - if anything other than user read/write,
|
|
* then throw a warning. This change was made to help ensure that the
|
|
* client consumes a proper rc file with strict permissions set (thanks
|
|
* to Fernando Arnaboldi from IOActive for pointing this out).
|
|
*/
|
|
if(verify_file_perms_ownership(rcfile) != 1)
|
|
exit(EXIT_FAILURE);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
keys_status(fko_cli_options_t *options)
|
|
{
|
|
FILE *key_gen_file_ptr = NULL;
|
|
char rcfile[MAX_PATH_LEN] = {0};
|
|
|
|
if(options->key_gen == 1)
|
|
{
|
|
if(options->key_gen_file[0] != '\0')
|
|
{
|
|
if ((key_gen_file_ptr = fopen(options->key_gen_file, "w")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Unable to create key gen file: %s: %s",
|
|
options->key_gen_file, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fprintf(key_gen_file_ptr, "KEY_BASE64: %s\nHMAC_KEY_BASE64: %s\n",
|
|
options->key_base64, options->hmac_key_base64);
|
|
fclose(key_gen_file_ptr);
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"[+] Wrote Rijndael and HMAC keys to: %s",
|
|
options->key_gen_file);
|
|
}
|
|
else
|
|
{
|
|
if(options->save_rc_stanza == 1)
|
|
{
|
|
set_rc_file(rcfile, options);
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"[+] Wrote Rijndael and HMAC keys to rc file: %s", rcfile);
|
|
}
|
|
else
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"KEY_BASE64: %s\nHMAC_KEY_BASE64: %s",
|
|
options->key_base64, options->hmac_key_base64);
|
|
}
|
|
|
|
/* Always exit out in --key-gen mode since the fwknopd server
|
|
* has no way to know what the new keys are
|
|
*/
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
|
|
/* Parse any time offset from the command line
|
|
*/
|
|
static int
|
|
parse_time_offset(const char *offset_str)
|
|
{
|
|
int i, j;
|
|
int offset = 0;
|
|
int offset_type = TIME_OFFSET_SECONDS;
|
|
int os_len = strlen(offset_str);
|
|
int is_err;
|
|
|
|
char offset_digits[MAX_TIME_STR_LEN] = {0};
|
|
|
|
j=0;
|
|
for (i=0; i < os_len; i++) {
|
|
if (isdigit(offset_str[i])) {
|
|
offset_digits[j] = offset_str[i];
|
|
j++;
|
|
if(j >= MAX_TIME_STR_LEN)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Invalid time offset: %s", offset_str);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} 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) {
|
|
log_msg(LOG_VERBOSITY_ERROR, "Invalid time offset: %s", offset_str);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
offset = strtol_wrapper(offset_digits, 0, (2 << 15),
|
|
EXIT_UPON_ERR, &is_err);
|
|
|
|
/* Apply the offset_type value
|
|
*/
|
|
offset *= offset_type;
|
|
|
|
return offset;
|
|
}
|
|
|
|
static int
|
|
create_fwknoprc(const char *rcfile)
|
|
{
|
|
FILE *rc = NULL;
|
|
int rcfile_fd = -1;
|
|
|
|
log_msg(LOG_VERBOSITY_NORMAL, "[*] Creating initial rc file: %s.", rcfile);
|
|
|
|
/* Try to create the initial rcfile with user read/write rights only.
|
|
* If the rcfile already exists, an error is returned */
|
|
rcfile_fd = open(rcfile, FWKNOPRC_OFLAGS ,FWKNOPRC_MODE);
|
|
|
|
// If an error occured ...
|
|
if (rcfile_fd == -1) {
|
|
log_msg(LOG_VERBOSITY_WARNING, "Unable to create initial rc file: %s: %s",
|
|
rcfile, strerror(errno));
|
|
return(-1);
|
|
}
|
|
|
|
// Free the rcfile descriptor
|
|
close(rcfile_fd);
|
|
|
|
if ((rc = fopen(rcfile, "w")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING, "Unable to write default setup to rcfile: %s: %s",
|
|
rcfile, strerror(errno));
|
|
return(-1);
|
|
}
|
|
|
|
fprintf(rc,
|
|
"# .fwknoprc\n"
|
|
"##############################################################################\n"
|
|
"#\n"
|
|
"# Firewall Knock Operator (fwknop) client rc file.\n"
|
|
"#\n"
|
|
"# This file contains user-specific fwknop client configuration default\n"
|
|
"# and named parameter sets for specific invocations of the fwknop client.\n"
|
|
"#\n"
|
|
"# Each section (or stanza) is identified and started by a line in this\n"
|
|
"# file that contains a single identifier surrounded by square brackets.\n"
|
|
"# It is this identifier (or name) that is used from the fwknop command line\n"
|
|
"# via the '-n <name>' argument to reference the corresponding stanza.\n"
|
|
"#\n"
|
|
"# The parameters within the stanza typically match corresponding client \n"
|
|
"# command-line parameters.\n"
|
|
"#\n"
|
|
"# The first one should always be `[default]' as it defines the global\n"
|
|
"# default settings for the user. These override the program defaults\n"
|
|
"# for these parameters. If a named stanza is used, its entries will\n"
|
|
"# override any of the default values. Command-line options will trump them\n"
|
|
"# all.\n"
|
|
"#\n"
|
|
"# Subsequent stanzas will have only the overriding and destination\n"
|
|
"# specific parameters.\n"
|
|
"#\n"
|
|
"# Lines starting with `#' and empty lines are ignored.\n"
|
|
"#\n"
|
|
"# See the fwknop.8 man page for a complete list of valid parameters\n"
|
|
"# and their values.\n"
|
|
"#\n"
|
|
"##############################################################################\n"
|
|
"#\n"
|
|
"# We start with the 'default' stanza. Uncomment and edit for your\n"
|
|
"# preferences. The client will use its built-in default for those items\n"
|
|
"# that are commented out.\n"
|
|
"#\n"
|
|
"[default]\n"
|
|
"\n"
|
|
"#DIGEST_TYPE sha256\n"
|
|
"#FW_TIMEOUT 30\n"
|
|
"#SPA_SERVER_PORT 62201\n"
|
|
"#SPA_SERVER_PROTO udp\n"
|
|
"#ALLOW_IP <ip addr>\n"
|
|
"#SPOOF_USER <username>\n"
|
|
"#SPOOF_SOURCE_IP <IPaddr>\n"
|
|
"#TIME_OFFSET 0\n"
|
|
"#USE_GPG N\n"
|
|
"#GPG_HOMEDIR /path/to/.gnupg\n"
|
|
"#GPG_SIGNER <signer ID>\n"
|
|
"#GPG_RECIPIENT <recipient ID>\n"
|
|
"\n"
|
|
"# User-provided named stanzas:\n"
|
|
"\n"
|
|
"# Example for a destination server of 192.168.1.20 to open access to\n"
|
|
"# SSH for an IP that is resolved externally, and one with a NAT request\n"
|
|
"# for a specific source IP that maps port 8088 on the server\n"
|
|
"# to port 88 on 192.168.1.55 with timeout.\n"
|
|
"#\n"
|
|
"#[myssh]\n"
|
|
"#SPA_SERVER 192.168.1.20\n"
|
|
"#ACCESS tcp/22\n"
|
|
"#ALLOW_IP resolve\n"
|
|
"#\n"
|
|
"#[mynatreq]\n"
|
|
"#SPA_SERVER 192.168.1.20\n"
|
|
"#ACCESS tcp/8088\n"
|
|
"#ALLOW_IP 10.21.2.6\n"
|
|
"#NAT_ACCESS 192.168.1.55,88\n"
|
|
"#CLIENT_TIMEOUT 60\n"
|
|
"#\n"
|
|
"\n"
|
|
);
|
|
|
|
fclose(rc);
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
parse_rc_param(fko_cli_options_t *options, const char *var_name, char * val)
|
|
{
|
|
int tmpint, is_err;
|
|
int parse_error = 0; /* 0 if the variable has been successfully processed, < 0 otherwise */
|
|
fko_var_t *var; /* Pointer on an fwknop variable structure */
|
|
|
|
log_msg(LOG_VERBOSITY_DEBUG, "parse_rc_param() : Parsing variable %s...", var_name);
|
|
|
|
/* Lookup the variable according to its name. */
|
|
var = lookup_var_by_name(var_name);
|
|
|
|
/* The variable is not handled if its pointer is NULL */
|
|
if (var == NULL)
|
|
parse_error = -1;
|
|
|
|
/* Digest Type */
|
|
else if (var->pos == FWKNOP_CLI_ARG_DIGEST_TYPE)
|
|
{
|
|
tmpint = digest_strtoint(val);
|
|
if(tmpint < 0)
|
|
parse_error = -1;
|
|
else
|
|
options->digest_type = tmpint;
|
|
}
|
|
/* Server protocol */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPA_SERVER_PROTO)
|
|
{
|
|
tmpint = proto_strtoint(val);
|
|
if(tmpint < 0)
|
|
parse_error = -1;
|
|
else
|
|
options->spa_proto = tmpint;
|
|
}
|
|
/* Server port */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPA_SERVER_PORT)
|
|
{
|
|
tmpint = strtol_wrapper(val, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err == FKO_SUCCESS)
|
|
options->spa_dst_port = tmpint;
|
|
else
|
|
parse_error = -1;
|
|
}
|
|
/* Source port */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPA_SOURCE_PORT)
|
|
{
|
|
tmpint = strtol_wrapper(val, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err == FKO_SUCCESS)
|
|
options->spa_src_port = tmpint;
|
|
else
|
|
parse_error = -1;
|
|
}
|
|
/* Firewall rule timeout */
|
|
else if (var->pos == FWKNOP_CLI_ARG_FW_TIMEOUT)
|
|
{
|
|
tmpint = strtol_wrapper(val, 0, (2 << 15), NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err == FKO_SUCCESS)
|
|
options->fw_timeout = tmpint;
|
|
else
|
|
parse_error = -1;
|
|
}
|
|
/* Allow IP */
|
|
else if (var->pos == FWKNOP_CLI_ARG_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", sizeof(options->allow_ip_str));
|
|
else if(strcasecmp(val, "resolve") == 0)
|
|
options->resolve_ip_http = 1;
|
|
else /* Assume IP address and validate */
|
|
{
|
|
strlcpy(options->allow_ip_str, val, sizeof(options->allow_ip_str));
|
|
if(! is_valid_ipv4_addr(options->allow_ip_str))
|
|
parse_error = -1;
|
|
}
|
|
}
|
|
/* Time Offset */
|
|
else if (var->pos == FWKNOP_CLI_ARG_TIME_OFFSET)
|
|
{
|
|
if(val[0] == '-')
|
|
{
|
|
val++;
|
|
options->time_offset_minus = parse_time_offset(val);
|
|
}
|
|
else
|
|
options->time_offset_plus = parse_time_offset(val);
|
|
}
|
|
/* symmetric encryption mode */
|
|
else if (var->pos == FWKNOP_CLI_ARG_ENCRYPTION_MODE)
|
|
{
|
|
tmpint = enc_mode_strtoint(val);
|
|
if(tmpint < 0)
|
|
parse_error = -1;
|
|
else
|
|
options->encryption_mode = tmpint;
|
|
}
|
|
/* Use GPG ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_USE_GPG)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->use_gpg = 1;
|
|
else;
|
|
}
|
|
/* Use GPG Agent ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_USE_GPG_AGENT)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->use_gpg_agent = 1;
|
|
else;
|
|
}
|
|
/* No GPG signing passphrase ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_NO_SIGNING_PW)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->gpg_no_signing_pw = 1;
|
|
else;
|
|
}
|
|
/* GPG Recipient */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_RECIPIENT)
|
|
{
|
|
strlcpy(options->gpg_recipient_key, val, sizeof(options->gpg_recipient_key));
|
|
}
|
|
/* GPG Signer */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_SIGNER)
|
|
{
|
|
strlcpy(options->gpg_signer_key, val, sizeof(options->gpg_signer_key));
|
|
}
|
|
/* GPG Homedir */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_HOMEDIR)
|
|
{
|
|
strlcpy(options->gpg_home_dir, val, sizeof(options->gpg_home_dir));
|
|
}
|
|
/* Spoof User */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPOOF_USER)
|
|
{
|
|
strlcpy(options->spoof_user, val, sizeof(options->spoof_user));
|
|
}
|
|
/* Spoof Source IP */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPOOF_SOURCE_IP)
|
|
{
|
|
strlcpy(options->spoof_ip_src_str, val, sizeof(options->spoof_ip_src_str));
|
|
}
|
|
/* ACCESS request */
|
|
else if (var->pos == FWKNOP_CLI_ARG_ACCESS)
|
|
{
|
|
strlcpy(options->access_str, val, sizeof(options->access_str));
|
|
}
|
|
/* SPA Server (destination) */
|
|
else if (var->pos == FWKNOP_CLI_ARG_SPA_SERVER)
|
|
{
|
|
strlcpy(options->spa_server_str, val, sizeof(options->spa_server_str));
|
|
}
|
|
/* Rand port ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_RAND_PORT)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->rand_port = 1;
|
|
else;
|
|
}
|
|
/* Rijndael key */
|
|
else if (var->pos == FWKNOP_CLI_ARG_KEY_RIJNDAEL)
|
|
{
|
|
strlcpy(options->key, val, sizeof(options->key));
|
|
options->have_key = 1;
|
|
}
|
|
/* Rijndael key (base-64 encoded) */
|
|
else if (var->pos == FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64)
|
|
{
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
parse_error = -1;
|
|
}
|
|
strlcpy(options->key_base64, val, sizeof(options->key_base64));
|
|
options->have_base64_key = 1;
|
|
}
|
|
/* GnuPG signing passphrase */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_SIGNING_PW)
|
|
{
|
|
strlcpy(options->key, val, sizeof(options->key));
|
|
options->have_key = 1;
|
|
}
|
|
/* GnuPG signing passphrase (base-64 encoded) */
|
|
else if (var->pos == FWKNOP_CLI_ARG_GPG_SIGNING_PW_BASE64)
|
|
{
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"GPG_SIGNING_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
parse_error = -1;
|
|
}
|
|
strlcpy(options->key_base64, val, sizeof(options->key_base64));
|
|
options->have_base64_key = 1;
|
|
}
|
|
/* HMAC digest type */
|
|
else if (var->pos == FWKNOP_CLI_ARG_HMAC_DIGEST_TYPE)
|
|
{
|
|
tmpint = hmac_digest_strtoint(val);
|
|
if(tmpint < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"HMAC_DIGEST_TYPE argument '%s' must be one of {md5,sha1,sha256,sha384,sha512}",
|
|
val);
|
|
parse_error = -1;
|
|
}
|
|
else
|
|
{
|
|
options->hmac_type = tmpint;
|
|
}
|
|
}
|
|
/* HMAC key (base64 encoded) */
|
|
else if (var->pos == FWKNOP_CLI_ARG_KEY_HMAC_BASE64)
|
|
{
|
|
if (! is_base64((unsigned char *) val, strlen(val)))
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.",
|
|
val);
|
|
parse_error = -1;
|
|
}
|
|
strlcpy(options->hmac_key_base64, val, sizeof(options->hmac_key_base64));
|
|
options->have_hmac_base64_key = 1;
|
|
options->use_hmac = 1;
|
|
}
|
|
|
|
/* HMAC key */
|
|
else if (var->pos == FWKNOP_CLI_ARG_KEY_HMAC)
|
|
{
|
|
strlcpy(options->hmac_key, val, sizeof(options->hmac_key));
|
|
options->have_hmac_key = 1;
|
|
options->use_hmac = 1;
|
|
}
|
|
|
|
/* --use-hmac */
|
|
else if (var->pos == FWKNOP_CLI_ARG_USE_HMAC)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->use_hmac = 1;
|
|
}
|
|
/* Key file */
|
|
else if (var->pos == FWKNOP_CLI_ARG_KEY_FILE)
|
|
{
|
|
strlcpy(options->get_key_file, val, sizeof(options->get_key_file));
|
|
}
|
|
/* HMAC key file */
|
|
else if (var->pos == FWKNOP_CLI_ARG_HMAC_KEY_FILE)
|
|
{
|
|
strlcpy(options->get_key_file, val,
|
|
sizeof(options->get_hmac_key_file));
|
|
}
|
|
/* NAT Access Request */
|
|
else if (var->pos == FWKNOP_CLI_ARG_NAT_ACCESS)
|
|
{
|
|
strlcpy(options->nat_access_str, val, sizeof(options->nat_access_str));
|
|
}
|
|
/* HTTP User Agent */
|
|
else if (var->pos == FWKNOP_CLI_ARG_HTTP_USER_AGENT)
|
|
{
|
|
strlcpy(options->http_user_agent, val, sizeof(options->http_user_agent));
|
|
}
|
|
/* Resolve URL */
|
|
else if (var->pos == FWKNOP_CLI_ARG_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)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,"Memory allocation error for resolve URL.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
strlcpy(options->resolve_url, val, tmpint);
|
|
}
|
|
/* NAT Local ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_NAT_LOCAL)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->nat_local = 1;
|
|
else;
|
|
}
|
|
/* NAT rand port ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_NAT_RAND_PORT)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->nat_rand_port = 1;
|
|
else;
|
|
}
|
|
/* NAT port */
|
|
else if (var->pos == FWKNOP_CLI_ARG_NAT_PORT)
|
|
{
|
|
tmpint = strtol_wrapper(val, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err == FKO_SUCCESS)
|
|
options->nat_port = tmpint;
|
|
else
|
|
parse_error = -1;
|
|
}
|
|
/* VERBOSE level */
|
|
else if (var->pos == FWKNOP_CLI_ARG_VERBOSE)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->verbose = 1;
|
|
else
|
|
{
|
|
tmpint = strtol_wrapper(val, 0, LOG_LAST_VERBOSITY - 1, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err == FKO_SUCCESS)
|
|
options->verbose = tmpint;
|
|
else
|
|
parse_error = -1;
|
|
}
|
|
|
|
if (parse_error == 0)
|
|
log_set_verbosity(LOG_DEFAULT_VERBOSITY + options->verbose);
|
|
}
|
|
/* RESOLVE_IP_HTTP ? */
|
|
else if (var->pos == FWKNOP_CLI_ARG_RESOLVE_IP_HTTP)
|
|
{
|
|
if (is_yes_str(val))
|
|
options->resolve_ip_http = 1;
|
|
else;
|
|
}
|
|
/* The variable is not a configuration variable */
|
|
else
|
|
{
|
|
parse_error = -1;
|
|
}
|
|
|
|
return(parse_error);
|
|
}
|
|
|
|
/**
|
|
* @brief Write a cli parameter to a file handle
|
|
*
|
|
* This function writes into a file handle a command line parameter
|
|
*
|
|
* @param fhandle File handle to write the new parameter to
|
|
* @param var_pos Variable position
|
|
* @param options FKO command line option structure
|
|
*/
|
|
static void
|
|
add_single_var_to_rc(FILE* fhandle, short var_pos, fko_cli_options_t *options)
|
|
{
|
|
char val[MAX_LINE_LEN] = {0};
|
|
fko_var_t *var;
|
|
|
|
var = lookup_var_by_position(var_pos);
|
|
|
|
if (var == NULL)
|
|
return;
|
|
|
|
if (fhandle == NULL)
|
|
return;
|
|
|
|
/* Select the argument to add and store its string value into val */
|
|
switch (var->pos)
|
|
{
|
|
case FWKNOP_CLI_ARG_DIGEST_TYPE :
|
|
digest_inttostr(options->digest_type, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPA_SERVER_PROTO :
|
|
proto_inttostr(options->spa_proto, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPA_SERVER_PORT :
|
|
snprintf(val, sizeof(val)-1, "%d", options->spa_dst_port);
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPA_SOURCE_PORT :
|
|
snprintf(val, sizeof(val)-1, "%d", options->spa_src_port);
|
|
break;
|
|
case FWKNOP_CLI_ARG_FW_TIMEOUT :
|
|
snprintf(val, sizeof(val)-1, "%d", options->fw_timeout);
|
|
break;
|
|
case FWKNOP_CLI_ARG_ALLOW_IP :
|
|
strlcpy(val, options->allow_ip_str, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_TIME_OFFSET :
|
|
if (options->time_offset_minus != 0)
|
|
snprintf(val, sizeof(val)-1, "-%d", options->time_offset_minus);
|
|
else if (options->time_offset_plus != 0)
|
|
snprintf(val, sizeof(val)-1, "%d", options->time_offset_plus);
|
|
else;
|
|
break;
|
|
case FWKNOP_CLI_ARG_ENCRYPTION_MODE :
|
|
enc_mode_inttostr(options->encryption_mode, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_USE_GPG :
|
|
bool_to_yesno(options->use_gpg, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_USE_GPG_AGENT :
|
|
bool_to_yesno(options->use_gpg_agent, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_GPG_RECIPIENT :
|
|
strlcpy(val, options->gpg_recipient_key, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_GPG_SIGNER :
|
|
strlcpy(val, options->gpg_signer_key, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_GPG_HOMEDIR :
|
|
strlcpy(val, options->gpg_home_dir, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_GPG_NO_SIGNING_PW :
|
|
bool_to_yesno(options->gpg_no_signing_pw, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPOOF_USER :
|
|
strlcpy(val, options->spoof_user, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPOOF_SOURCE_IP :
|
|
strlcpy(val, options->spoof_ip_src_str, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_ACCESS :
|
|
strlcpy(val, options->access_str, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_SPA_SERVER :
|
|
strlcpy(val, options->spa_server_str, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_RAND_PORT :
|
|
bool_to_yesno(options->rand_port, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_KEY_FILE :
|
|
strlcpy(val, options->get_key_file, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_HMAC_KEY_FILE :
|
|
strlcpy(val, options->get_hmac_key_file, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_KEY_RIJNDAEL:
|
|
strlcpy(val, options->key, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64:
|
|
strlcpy(val, options->key_base64, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_KEY_HMAC_BASE64:
|
|
strlcpy(val, options->hmac_key_base64, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_KEY_HMAC:
|
|
strlcpy(val, options->hmac_key, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_HMAC_DIGEST_TYPE :
|
|
hmac_digest_inttostr(options->hmac_type, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_USE_HMAC :
|
|
bool_to_yesno(options->use_hmac, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_NAT_ACCESS :
|
|
strlcpy(val, options->nat_access_str, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_HTTP_USER_AGENT :
|
|
strlcpy(val, options->http_user_agent, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_RESOLVE_URL :
|
|
if (options->resolve_url != NULL)
|
|
strlcpy(val, options->resolve_url, sizeof(val));
|
|
else;
|
|
break;
|
|
case FWKNOP_CLI_ARG_NAT_LOCAL :
|
|
bool_to_yesno(options->nat_local, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_NAT_RAND_PORT :
|
|
bool_to_yesno(options->nat_rand_port, val, sizeof(val));
|
|
break;
|
|
case FWKNOP_CLI_ARG_NAT_PORT :
|
|
snprintf(val, sizeof(val)-1, "%d", options->nat_port);
|
|
break;
|
|
case FWKNOP_CLI_ARG_VERBOSE:
|
|
if((options->verbose == 0) || (options->verbose == 1))
|
|
bool_to_yesno(options->verbose, val, sizeof(val));
|
|
else
|
|
snprintf(val, sizeof(val)-1, "%d", options->verbose);
|
|
break;
|
|
case FWKNOP_CLI_ARG_RESOLVE_IP_HTTP:
|
|
bool_to_yesno(options->resolve_ip_http, val, sizeof(val));
|
|
break;
|
|
default:
|
|
log_msg(LOG_VERBOSITY_WARNING, "Warning from add_single_var_to_rc() : Bad variable position %u", var->pos);
|
|
return;
|
|
}
|
|
|
|
log_msg(LOG_VERBOSITY_DEBUG, "add_single_var_to_rc() : Updating param (%u) %s to %s",
|
|
var->pos, var->name, val);
|
|
|
|
fprintf(fhandle, RC_PARAM_TEMPLATE, var->name, val);
|
|
}
|
|
|
|
/**
|
|
* @brief Add configuration variables in a file
|
|
*
|
|
* The parameters are selected by a bitmask and extracted from the
|
|
* fko_cli_options_t structure.
|
|
*
|
|
* @param rc File handle on the file to write to
|
|
* @param options fko_cli_options_t structure containing the values of the parameters
|
|
* @param bitmask Bitmask used to select the parameters to add
|
|
*/
|
|
static void
|
|
add_multiple_vars_to_rc(FILE* rc, fko_cli_options_t *options, fko_var_bitmask_t *bitmask)
|
|
{
|
|
short ndx = 0; /* Index of a configuration variable in fko_var_array table */
|
|
short position; /* Position of the configuration variable */
|
|
|
|
for (ndx=0 ; ndx<ARRAY_SIZE(fko_var_array) ; ndx++)
|
|
{
|
|
position = fko_var_array[ndx].pos;
|
|
if (bitmask_has_var(position, bitmask))
|
|
add_single_var_to_rc(rc, position, options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Process the fwknoprc file and lookup a section to extract its settings.
|
|
*
|
|
* This function aims at loading the settings for a specific section in
|
|
* a fwknoprc file.
|
|
*
|
|
* @param section_name Name of the section to lookup.
|
|
* @param options Fwknop option structure where settings have to
|
|
* be stored.
|
|
*
|
|
* @return 0 if the section has been found and processed successfully
|
|
* a negative value if one or more errors occured
|
|
*/
|
|
static int
|
|
process_rc_section(char *section_name, fko_cli_options_t *options)
|
|
{
|
|
FILE *rc;
|
|
int line_num = 0, do_exit = 0;
|
|
char line[MAX_LINE_LEN] = {0};
|
|
char rcfile[MAX_PATH_LEN] = {0};
|
|
char curr_stanza[MAX_LINE_LEN] = {0};
|
|
rc_file_param_t param;
|
|
int rc_section_found = 0;
|
|
|
|
set_rc_file(rcfile, options);
|
|
|
|
/* Open the rc file for reading, if it does not exist, then create
|
|
* an initial .fwknoprc file with defaults and go on.
|
|
*/
|
|
if ((rc = fopen(rcfile, "r")) == NULL)
|
|
{
|
|
if(errno == ENOENT)
|
|
{
|
|
if(create_fwknoprc(rcfile) != 0)
|
|
return -1;
|
|
}
|
|
else
|
|
log_msg(LOG_VERBOSITY_WARNING, "Unable to open rc file: %s: %s",
|
|
rcfile, strerror(errno));
|
|
|
|
return -1;
|
|
}
|
|
|
|
log_msg(LOG_VERBOSITY_DEBUG, "process_rc_section() : Parsing section '%s' ...",
|
|
section_name);
|
|
|
|
while ((fgets(line, MAX_LINE_LEN, rc)) != NULL)
|
|
{
|
|
line_num++;
|
|
line[MAX_LINE_LEN-1] = '\0';
|
|
|
|
/* Get past comments and empty lines (note: we only look at the
|
|
* first character.
|
|
*/
|
|
if(IS_EMPTY_LINE(line[0]))
|
|
continue;
|
|
|
|
/* Check which section we are working on */
|
|
if (is_rc_section(line, strlen(line), curr_stanza, sizeof(curr_stanza)))
|
|
{
|
|
rc_section_found = (strcasecmp(curr_stanza, section_name) == 0) ? 1 : 0;
|
|
|
|
if (strcasecmp(curr_stanza, options->use_rc_stanza) == 0)
|
|
options->got_named_stanza = 1;
|
|
|
|
continue;
|
|
}
|
|
|
|
/* We are not in the good section */
|
|
else if (rc_section_found == 0)
|
|
continue;
|
|
|
|
/* We have not found a valid parameter */
|
|
else if (is_rc_param(line, ¶m) == 0)
|
|
continue;
|
|
|
|
/* We have a valid parameter */
|
|
else
|
|
{
|
|
if(parse_rc_param(options, param.name, param.val) < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"Parameter error in %s, line %i: var=%s, val=%s",
|
|
rcfile, line_num, param.name, param.val);
|
|
do_exit = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(rc);
|
|
|
|
if (do_exit)
|
|
exit(EXIT_FAILURE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Update the user rc file with the new parameters for a selected stanza.
|
|
*
|
|
* This function writes the new configuration in a temporary file and renames it
|
|
* as the new rc file afterwards. All of the previous parameters for the
|
|
* selected stanza are removed and replaced by the arguments from the command
|
|
* line.
|
|
*
|
|
* @param options structure containing all of the fko settings
|
|
* @param args_bitmask command line argument bitmask
|
|
*/
|
|
static void
|
|
update_rc(fko_cli_options_t *options, fko_var_bitmask_t *bitmask)
|
|
{
|
|
FILE *rc;
|
|
FILE *rc_update;
|
|
int rcfile_fd = -1;
|
|
int stanza_found = 0;
|
|
int stanza_updated = 0;
|
|
char line[MAX_LINE_LEN] = {0};
|
|
char rcfile[MAX_PATH_LEN] = {0};
|
|
char rcfile_update[MAX_PATH_LEN] = {0};
|
|
char curr_stanza[MAX_LINE_LEN] = {0};
|
|
rc_file_param_t param; /* Structure to contain a conf. variable name with its value */
|
|
fko_var_t *var;
|
|
|
|
set_rc_file(rcfile, options);
|
|
|
|
strlcpy(rcfile_update, rcfile, sizeof(rcfile_update));
|
|
strlcat(rcfile_update, ".updated", sizeof(rcfile_update));
|
|
|
|
/* Create a new temporary rc file */
|
|
rcfile_fd = open(rcfile_update, FWKNOPRC_OFLAGS, FWKNOPRC_MODE);
|
|
if (rcfile_fd == -1)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"update_rc() : Unable to create temporary rc file: %s: %s",
|
|
rcfile_update, strerror(errno));
|
|
return;
|
|
}
|
|
close(rcfile_fd);
|
|
|
|
/* Open the current rcfile and a temporary one respectively in read and
|
|
* write mode */
|
|
if ((rc = fopen(rcfile, "r")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"update_rc() : Unable to open rc file: %s: %s",
|
|
rcfile, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
if ((rc_update = fopen(rcfile_update, "w")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"update_rc() : Unable to open rc file: %s: %s",
|
|
rcfile_update, strerror(errno));
|
|
fclose(rc);
|
|
return;
|
|
}
|
|
|
|
/* Go through the file line by line */
|
|
stanza_found = 0;
|
|
while ((fgets(line, MAX_LINE_LEN, rc)) != NULL)
|
|
{
|
|
line[MAX_LINE_LEN-1] = '\0';
|
|
|
|
/* If we find a section... */
|
|
if(is_rc_section(line, strlen(line), curr_stanza, sizeof(curr_stanza)) == 1)
|
|
{
|
|
/* and we have already parsed the section we wanted to save, we
|
|
* can update our parameters */
|
|
if (stanza_found)
|
|
{
|
|
log_msg(LOG_VERBOSITY_DEBUG, "update_rc() : Updating %s stanza", options->use_rc_stanza);
|
|
add_multiple_vars_to_rc(rc_update, options, bitmask);
|
|
fprintf(rc_update, "\n");
|
|
stanza_found = 0;
|
|
stanza_updated = 1;
|
|
}
|
|
|
|
/* and this is the one we are looking for, we set the stanza
|
|
* as found */
|
|
else if (strncasecmp(curr_stanza, options->use_rc_stanza, MAX_LINE_LEN) == 0)
|
|
stanza_found = 1;
|
|
|
|
/* otherwise we disable the stanza */
|
|
else
|
|
stanza_found = 0;
|
|
}
|
|
|
|
/* If we are processing a parameter for our stanza */
|
|
else if (stanza_found)
|
|
{
|
|
/* and the user has specified a force option, there is no need to
|
|
* check for critical variables */
|
|
if (options->force_save_rc_stanza)
|
|
continue;
|
|
|
|
/* ask the user what to do with the critical var found in the
|
|
* rcfile */
|
|
else if (is_rc_param(line, ¶m))
|
|
{
|
|
if ( ((var=lookup_var_by_name(param.name)) != NULL)
|
|
&& var_is_critical(var->pos) )
|
|
{
|
|
if (ask_overwrite_var(var->name, curr_stanza))
|
|
continue;
|
|
else
|
|
remove_var_from_bitmask(var->pos, bitmask);
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
|
|
/* discard all other lines */
|
|
else
|
|
continue;
|
|
}
|
|
|
|
/* We re not processing any important variables from our stanza and no new
|
|
* stanza */
|
|
else;
|
|
|
|
/* Add the line to the new rcfile */
|
|
fprintf(rc_update, "%s", line);
|
|
}
|
|
|
|
/* The configuration has not been updated yet */
|
|
if (stanza_updated == 0)
|
|
{
|
|
/* but the stanza has been found, We update it now. */
|
|
if (stanza_found == 1)
|
|
log_msg(LOG_VERBOSITY_DEBUG, "update_rc() : Updating %s stanza",
|
|
options->use_rc_stanza);
|
|
|
|
/* otherwise we append the new settings to the file */
|
|
else
|
|
{
|
|
fprintf(rc_update, "\n");
|
|
log_msg(LOG_VERBOSITY_DEBUG, "update_rc() : Inserting new %s stanza",
|
|
options->use_rc_stanza);
|
|
fprintf(rc_update, RC_SECTION_TEMPLATE, options->use_rc_stanza);
|
|
}
|
|
|
|
add_multiple_vars_to_rc(rc_update, options, bitmask);
|
|
}
|
|
|
|
/* otherwise we have already done everything. Nothing to do. */
|
|
else;
|
|
|
|
/* Close file handles */
|
|
fclose(rc);
|
|
fclose(rc_update);
|
|
|
|
/* Renamed the temporary file as the new rc file */
|
|
if (remove(rcfile) != 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"update_rc() : Unable to remove %s to %s : %s",
|
|
rcfile_update, rcfile, strerror(errno));
|
|
}
|
|
|
|
if (rename(rcfile_update, rcfile) != 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"update_rc() : Unable to rename %s to %s",
|
|
rcfile_update, rcfile);
|
|
}
|
|
}
|
|
|
|
/* Sanity and bounds checks for the various options.
|
|
*/
|
|
static void
|
|
validate_options(fko_cli_options_t *options)
|
|
{
|
|
|
|
if ( (options->use_rc_stanza[0] != 0x0)
|
|
&& (options->got_named_stanza == 0)
|
|
&& (options->save_rc_stanza == 0) )
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Named configuration stanza: [%s] was not found.",
|
|
options->use_rc_stanza);
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if ( (options->save_rc_stanza == 1) && (options->use_rc_stanza[0] == 0) )
|
|
{
|
|
/* Set the stanza name to the -D arg value
|
|
*/
|
|
if (options->spa_server_str[0] == 0x0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must use --destination unless --test mode is used");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
strlcpy(options->use_rc_stanza, options->spa_server_str, sizeof(options->use_rc_stanza));
|
|
}
|
|
|
|
/* 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->key_gen
|
|
&& !options->version
|
|
&& !options->show_last_command
|
|
&& !options->run_last_command)
|
|
{
|
|
if (options->spa_server_str[0] == 0x0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must use --destination unless --test mode is used");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (options->resolve_url != NULL)
|
|
options->resolve_ip_http = 1;
|
|
|
|
if (!options->resolve_ip_http)
|
|
{
|
|
if(options->allow_ip_str[0] == 0x0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must use one of [-s|-R|-a] to specify IP for SPA access.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
else if(options->verbose
|
|
&& strncmp(options->allow_ip_str, "0.0.0.0", strlen("0.0.0.0")) == 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_WARNING,
|
|
"[-] WARNING: Should use -a or -R to harden SPA against potential MITM attacks");
|
|
}
|
|
|
|
if(! is_valid_ipv4_addr(options->allow_ip_str))
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Invalid allow IP specified for SPA access");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (options->spoof_ip_src_str[0] != 0x00)
|
|
{
|
|
if(! is_valid_ipv4_addr(options->spoof_ip_src_str))
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Invalid spoof IP");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if(options->spa_proto != FKO_PROTO_TCP_RAW
|
|
&& options->spa_proto != FKO_PROTO_UDP_RAW
|
|
&& options->spa_proto != FKO_PROTO_ICMP)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must set -Q <udpraw|tcpraw|icmp> with a spoofed source IP");
|
|
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(options->http_proxy[0] != 0x0 && options->spa_proto != FKO_PROTO_HTTP)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Cannot set --http-proxy with a non-HTTP protocol.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* If we are using gpg, we must at least have the recipient set.
|
|
*/
|
|
if(options->use_gpg)
|
|
{
|
|
if(strlen(options->gpg_recipient_key) == 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must specify --gpg-recipient-key when GPG is used.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if(options->encryption_mode == FKO_ENC_MODE_ASYMMETRIC
|
|
&& ! options->use_gpg)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Must specify GPG recipient/signing keys when Asymmetric encryption mode is used.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if(options->encryption_mode == FKO_ENC_MODE_CBC_LEGACY_IV
|
|
&& options->use_hmac)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Legacy encryption mode is incompatible with HMAC usage.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Validate HMAC digest type
|
|
*/
|
|
if(options->use_hmac && options->hmac_type == FKO_HMAC_UNKNOWN)
|
|
options->hmac_type = FKO_DEFAULT_HMAC_MODE;
|
|
|
|
if(options->key_gen && options->hmac_type == FKO_HMAC_UNKNOWN)
|
|
options->hmac_type = FKO_DEFAULT_HMAC_MODE;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Establish a few defaults such as UDP/62201 for sending the SPA
|
|
* packet (can be changed with --server-proto/--server-port)
|
|
*/
|
|
static void
|
|
set_defaults(fko_cli_options_t *options)
|
|
{
|
|
options->spa_proto = FKO_DEFAULT_PROTO;
|
|
options->spa_dst_port = FKO_DEFAULT_PORT;
|
|
options->fw_timeout = -1;
|
|
|
|
options->key_len = FKO_DEFAULT_KEY_LEN;
|
|
options->hmac_key_len = FKO_DEFAULT_HMAC_KEY_LEN;
|
|
options->hmac_type = FKO_HMAC_UNKNOWN; /* updated when HMAC key is used */
|
|
|
|
options->spa_icmp_type = ICMP_ECHOREPLY; /* only used in '-P icmp' mode */
|
|
options->spa_icmp_code = 0; /* only used in '-P icmp' mode */
|
|
|
|
options->input_fd = FD_INVALID;
|
|
|
|
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, is_err;
|
|
fko_var_bitmask_t var_bitmask;
|
|
char rcfile[MAX_PATH_LEN] = {0};
|
|
|
|
/* Zero out options, opts_track and bitmask.
|
|
*/
|
|
memset(options, 0x00, sizeof(fko_cli_options_t));
|
|
memset(&var_bitmask, 0x00, sizeof(fko_var_bitmask_t));
|
|
|
|
/* Make sure a few reasonable defaults are set
|
|
*/
|
|
set_defaults(options);
|
|
|
|
/* First pass over cmd_line args to see if a named-stanza in the
|
|
* rc file is used.
|
|
*/
|
|
while ((cmd_arg = getopt_long(argc, argv,
|
|
GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) {
|
|
switch(cmd_arg) {
|
|
case 'h':
|
|
usage();
|
|
exit(EXIT_SUCCESS);
|
|
case NO_SAVE_ARGS:
|
|
options->no_save_args = 1;
|
|
break;
|
|
case 'n':
|
|
strlcpy(options->use_rc_stanza, optarg, sizeof(options->use_rc_stanza));
|
|
break;
|
|
case SAVE_RC_STANZA:
|
|
options->save_rc_stanza = 1;
|
|
break;
|
|
case STANZA_LIST:
|
|
options->stanza_list = 1;
|
|
break;
|
|
case 'E':
|
|
strlcpy(options->args_save_file, optarg, sizeof(options->args_save_file));
|
|
break;
|
|
case RC_FILE_PATH:
|
|
strlcpy(options->rc_file, optarg, sizeof(options->rc_file));
|
|
break;
|
|
case 'v':
|
|
options->verbose++;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_VERBOSE, &var_bitmask);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Update the verbosity level for the log module */
|
|
log_set_verbosity(LOG_DEFAULT_VERBOSITY + options->verbose);
|
|
|
|
/* Dump the configured stanzas from an rcfile */
|
|
if (options->stanza_list == 1)
|
|
{
|
|
set_rc_file(rcfile, options);
|
|
dump_configured_stanzas_from_rcfile(rcfile);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/* First process the .fwknoprc file.
|
|
*/
|
|
process_rc_section(RC_SECTION_DEFAULT, options);
|
|
|
|
/* Load the user specified stanza from .fwknoprc file */
|
|
if ( (options->got_named_stanza) && (options->save_rc_stanza == 0) )
|
|
process_rc_section(options->use_rc_stanza, options);
|
|
|
|
/* Reset the options index so we can run through them again.
|
|
*/
|
|
optind = 0;
|
|
|
|
while ((cmd_arg = getopt_long(argc, argv,
|
|
GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) {
|
|
|
|
switch(cmd_arg) {
|
|
case 'a':
|
|
strlcpy(options->allow_ip_str, optarg, sizeof(options->allow_ip_str));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_ALLOW_IP, &var_bitmask);
|
|
break;
|
|
case 'A':
|
|
strlcpy(options->access_str, optarg, sizeof(options->access_str));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_ACCESS, &var_bitmask);
|
|
break;
|
|
case 'b':
|
|
options->save_packet_file_append = 1;
|
|
break;
|
|
case 'B':
|
|
strlcpy(options->save_packet_file, optarg, sizeof(options->save_packet_file));
|
|
break;
|
|
case 'C':
|
|
strlcpy(options->server_command, optarg, sizeof(options->server_command));
|
|
break;
|
|
case 'D':
|
|
strlcpy(options->spa_server_str, optarg, sizeof(options->spa_server_str));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPA_SERVER, &var_bitmask);
|
|
break;
|
|
case 'E':
|
|
strlcpy(options->args_save_file, optarg, sizeof(options->args_save_file));
|
|
break;
|
|
case 'f':
|
|
options->fw_timeout = strtol_wrapper(optarg, 0,
|
|
(2 << 16), NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "--fw-timeout must be within [%d-%d]",
|
|
0, (2 << 16));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_FW_TIMEOUT, &var_bitmask);
|
|
break;
|
|
case 'g':
|
|
case GPG_ENCRYPTION:
|
|
options->use_gpg = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
break;
|
|
case 'G':
|
|
strlcpy(options->get_key_file, optarg, sizeof(options->get_key_file));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_FILE, &var_bitmask);
|
|
break;
|
|
case GET_HMAC_KEY:
|
|
strlcpy(options->get_hmac_key_file, optarg,
|
|
sizeof(options->get_hmac_key_file));
|
|
options->use_hmac = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_HMAC_KEY_FILE, &var_bitmask);
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
exit(EXIT_SUCCESS);
|
|
case 'H':
|
|
options->spa_proto = FKO_PROTO_HTTP;
|
|
strlcpy(options->http_proxy, optarg, sizeof(options->http_proxy));
|
|
break;
|
|
case 'k':
|
|
options->key_gen = 1;
|
|
break;
|
|
case 'K':
|
|
options->key_gen = 1;
|
|
strlcpy(options->key_gen_file, optarg, sizeof(options->key_gen_file));
|
|
break;
|
|
case KEY_RIJNDAEL:
|
|
strlcpy(options->key, optarg, sizeof(options->key));
|
|
options->have_key = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_RIJNDAEL, &var_bitmask);
|
|
break;
|
|
case KEY_RIJNDAEL_BASE64:
|
|
if (! is_base64((unsigned char *) optarg, strlen(optarg)))
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Base64 encoded Rijndael argument '%s' doesn't look like base64-encoded data.",
|
|
optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
strlcpy(options->key_base64, optarg, sizeof(options->key_base64));
|
|
options->have_base64_key = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64, &var_bitmask);
|
|
break;
|
|
case KEY_HMAC_BASE64:
|
|
if (! is_base64((unsigned char *) optarg, strlen(optarg)))
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Base64 encoded HMAC argument '%s' doesn't look like base64-encoded data.",
|
|
optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
strlcpy(options->hmac_key_base64, optarg, sizeof(options->hmac_key_base64));
|
|
options->have_hmac_base64_key = 1;
|
|
options->use_hmac = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_HMAC_BASE64, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_HMAC, &var_bitmask);
|
|
break;
|
|
case KEY_HMAC:
|
|
strlcpy(options->hmac_key, optarg, sizeof(options->hmac_key));
|
|
options->have_hmac_key = 1;
|
|
options->use_hmac = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_HMAC, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_HMAC, &var_bitmask);
|
|
break;
|
|
case KEY_LEN:
|
|
options->key_len = strtol_wrapper(optarg, 1,
|
|
MAX_KEY_LEN, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Invalid key length '%s', must be in [%d-%d]",
|
|
optarg, 1, MAX_KEY_LEN);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case HMAC_DIGEST_TYPE:
|
|
if((options->hmac_type = hmac_digest_strtoint(optarg)) < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"* Invalid hmac digest type: %s, use {md5,sha1,sha256,sha384,sha512}",
|
|
optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_HMAC_DIGEST_TYPE, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_HMAC, &var_bitmask);
|
|
options->use_hmac = 1;
|
|
break;
|
|
case HMAC_KEY_LEN:
|
|
options->hmac_key_len = strtol_wrapper(optarg, 1,
|
|
MAX_KEY_LEN, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Invalid hmac key length '%s', must be in [%d-%d]",
|
|
optarg, 1, MAX_KEY_LEN);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_HMAC, &var_bitmask);
|
|
options->use_hmac = 1;
|
|
break;
|
|
case SPA_ICMP_TYPE:
|
|
options->spa_icmp_type = strtol_wrapper(optarg, 0,
|
|
MAX_ICMP_TYPE, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Invalid icmp type '%s', must be in [%d-%d]",
|
|
optarg, 0, MAX_ICMP_TYPE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case SPA_ICMP_CODE:
|
|
options->spa_icmp_code = strtol_wrapper(optarg, 0,
|
|
MAX_ICMP_CODE, NO_EXIT_UPON_ERR, &is_err);
|
|
if(is_err != FKO_SUCCESS)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"Invalid icmp code '%s', must be in [%d-%d]",
|
|
optarg, 0, MAX_ICMP_CODE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'l':
|
|
options->run_last_command = 1;
|
|
break;
|
|
case 'm':
|
|
case FKO_DIGEST_NAME:
|
|
if((options->digest_type = digest_strtoint(optarg)) < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"* Invalid digest type: %s, use {md5,sha1,sha256,sha384,sha512}",
|
|
optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_DIGEST_TYPE, &var_bitmask);
|
|
break;
|
|
case 'M':
|
|
case ENCRYPTION_MODE:
|
|
if((options->encryption_mode = enc_mode_strtoint(optarg)) < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR,
|
|
"* Invalid encryption mode: %s, use {CBC,CTR,legacy,Asymmetric}",
|
|
optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_ENCRYPTION_MODE, &var_bitmask);
|
|
break;
|
|
case NO_SAVE_ARGS:
|
|
options->no_save_args = 1;
|
|
break;
|
|
case 'n':
|
|
/* We already handled this earlier, so we do nothing here
|
|
*/
|
|
break;
|
|
case 'N':
|
|
strlcpy(options->nat_access_str, optarg, sizeof(options->nat_access_str));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_NAT_ACCESS, &var_bitmask);
|
|
break;
|
|
case 'p':
|
|
options->spa_dst_port = strtol_wrapper(optarg, 0,
|
|
MAX_PORT, EXIT_UPON_ERR, &is_err);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPA_SERVER_PORT, &var_bitmask);
|
|
break;
|
|
case 'P':
|
|
if((options->spa_proto = proto_strtoint(optarg)) < 0)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Unrecognized protocol: %s", optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPA_SERVER_PROTO, &var_bitmask);
|
|
break;
|
|
case 'Q':
|
|
strlcpy(options->spoof_ip_src_str, optarg, sizeof(options->spoof_ip_src_str));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPOOF_SOURCE_IP, &var_bitmask);
|
|
break;
|
|
case RC_FILE_PATH:
|
|
strlcpy(options->rc_file, optarg, sizeof(options->rc_file));
|
|
break;
|
|
case 'r':
|
|
options->rand_port = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_RAND_PORT, &var_bitmask);
|
|
break;
|
|
case 'R':
|
|
options->resolve_ip_http = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_RESOLVE_IP_HTTP, &var_bitmask);
|
|
break;
|
|
case RESOLVE_URL:
|
|
if(options->resolve_url != NULL)
|
|
free(options->resolve_url);
|
|
options->resolve_url = malloc(strlen(optarg)+1);
|
|
if(options->resolve_url == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Memory allocation error for resolve URL.");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
strlcpy(options->resolve_url, optarg, strlen(optarg)+1);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_RESOLVE_URL, &var_bitmask);
|
|
break;
|
|
case SHOW_LAST_ARGS:
|
|
options->show_last_command = 1;
|
|
break;
|
|
case 's':
|
|
strlcpy(options->allow_ip_str, "0.0.0.0", sizeof(options->allow_ip_str));
|
|
break;
|
|
case 'S':
|
|
options->spa_src_port = strtol_wrapper(optarg, 0,
|
|
MAX_PORT, EXIT_UPON_ERR, &is_err);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPA_SOURCE_PORT, &var_bitmask);
|
|
break;
|
|
case SAVE_RC_STANZA:
|
|
/* We already handled this earlier, so we do nothing here
|
|
*/
|
|
break;
|
|
case 'T':
|
|
options->test = 1;
|
|
break;
|
|
case 'u':
|
|
strlcpy(options->http_user_agent, optarg, sizeof(options->http_user_agent));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_HTTP_USER_AGENT, &var_bitmask);
|
|
break;
|
|
case 'U':
|
|
strlcpy(options->spoof_user, optarg, sizeof(options->spoof_user));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_SPOOF_USER, &var_bitmask);
|
|
break;
|
|
case 'v':
|
|
/* Handled earlier.
|
|
*/
|
|
break;
|
|
case 'V':
|
|
options->version = 1;
|
|
break;
|
|
case GPG_RECIP_KEY:
|
|
options->use_gpg = 1;
|
|
strlcpy(options->gpg_recipient_key, optarg, sizeof(options->gpg_recipient_key));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_GPG_RECIPIENT, &var_bitmask);
|
|
break;
|
|
case GPG_SIGNER_KEY:
|
|
options->use_gpg = 1;
|
|
strlcpy(options->gpg_signer_key, optarg, sizeof(options->gpg_signer_key));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_GPG_SIGNER, &var_bitmask);
|
|
break;
|
|
case GPG_HOME_DIR:
|
|
options->use_gpg = 1;
|
|
strlcpy(options->gpg_home_dir, optarg, sizeof(options->gpg_home_dir));
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_GPG_HOMEDIR, &var_bitmask);
|
|
break;
|
|
case GPG_AGENT:
|
|
options->use_gpg = 1;
|
|
options->use_gpg_agent = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG_AGENT, &var_bitmask);
|
|
break;
|
|
case GPG_ALLOW_NO_SIGNING_PW:
|
|
options->use_gpg = 1;
|
|
options->gpg_no_signing_pw = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_GPG, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_GPG_NO_SIGNING_PW, &var_bitmask);
|
|
break;
|
|
case NAT_LOCAL:
|
|
options->nat_local = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_NAT_LOCAL, &var_bitmask);
|
|
break;
|
|
case NAT_RAND_PORT:
|
|
options->nat_rand_port = 1;
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_NAT_RAND_PORT, &var_bitmask);
|
|
break;
|
|
case NAT_PORT:
|
|
options->nat_port = strtol_wrapper(optarg, 0,
|
|
MAX_PORT, EXIT_UPON_ERR, &is_err);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_NAT_PORT, &var_bitmask);
|
|
break;
|
|
case TIME_OFFSET_PLUS:
|
|
options->time_offset_plus = parse_time_offset(optarg);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_TIME_OFFSET, &var_bitmask);
|
|
break;
|
|
case TIME_OFFSET_MINUS:
|
|
options->time_offset_minus = parse_time_offset(optarg);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_TIME_OFFSET, &var_bitmask);
|
|
break;
|
|
case USE_HMAC:
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_USE_HMAC, &var_bitmask);
|
|
options->use_hmac = 1;
|
|
break;
|
|
case FORCE_SAVE_RC_STANZA:
|
|
options->force_save_rc_stanza = 1;
|
|
break;
|
|
case FD_SET_STDIN:
|
|
options->input_fd = STDIN_FILENO;
|
|
break;
|
|
case FD_SET_ALT:
|
|
#ifdef WIN32
|
|
log_msg(LOG_VERBOSITY_ERROR, "Read password from FD not supported on Windows");
|
|
exit(EXIT_FAILURE);
|
|
#endif
|
|
options->input_fd = strtol_wrapper(optarg, 0,
|
|
-1, EXIT_UPON_ERR, &is_err);
|
|
break;
|
|
default:
|
|
usage();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Now that we have all of our options set, we can validate them */
|
|
validate_options(options);
|
|
|
|
/* Generate Rijndael + HMAC keys from /dev/random and base64 encode
|
|
*/
|
|
generate_keys(options);
|
|
|
|
/* We can upgrade our settings with the parameters set on the command
|
|
* line by the user */
|
|
if (options->save_rc_stanza == 1)
|
|
{
|
|
/* If we are asked to generate keys, we add them to the bitmask so
|
|
* that they can be added to the stanza when updated */
|
|
if (options->key_gen == 1)
|
|
{
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_RIJNDAEL_BASE64, &var_bitmask);
|
|
add_var_to_bitmask(FWKNOP_CLI_ARG_KEY_HMAC_BASE64, &var_bitmask);
|
|
}
|
|
else;
|
|
|
|
update_rc(options, &var_bitmask);
|
|
}
|
|
else;
|
|
|
|
keys_status(options);
|
|
|
|
return;
|
|
}
|
|
|
|
/* Print usage message...
|
|
*/
|
|
void
|
|
usage(void)
|
|
{
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"\n%s client version %s\n%s - http://%s/fwknop/\n",
|
|
MY_NAME, MY_VERSION, MY_DESC, HTTP_RESOLVE_HOST);
|
|
log_msg(LOG_VERBOSITY_NORMAL,
|
|
"Usage: fwknop -A <port list> [-s|-R|-a] -D <spa_server> [options]\n\n"
|
|
" -n, --named-config Specify a named configuration stanza in the\n"
|
|
" '$HOME/.fwknoprc' file to provide some of all\n"
|
|
" of the configuration parameters.\n"
|
|
" If more arguments are set through the command\n"
|
|
" line, the configuration is updated accordingly\n"
|
|
" -A, --access Provide a list of ports/protocols to open\n"
|
|
" on the server (e.g. 'tcp/22').\n"
|
|
" -a, --allow-ip Specify IP address to allow within the SPA\n"
|
|
" packet (e.g. '123.2.3.4'). If \n"
|
|
" -D, --destination Specify the hostname or IP address of the\n"
|
|
" fwknop server.\n"
|
|
" --use-hmac Add an HMAC to the outbound SPA packet for\n"
|
|
" authenticated encryption.\n"
|
|
" -h, --help Print this usage message and exit.\n"
|
|
" -B, --save-packet Save the generated packet data to the\n"
|
|
" specified file.\n"
|
|
" -b, --save-packet-append Append the generated packet data to the\n"
|
|
" file specified with the -B option.\n"
|
|
" -C, --server-cmd Specify a command that the fwknop server will\n"
|
|
" execute on behalf of the fwknop client..\n"
|
|
" -N, --nat-access Gain NAT access to an internal service.\n"
|
|
" -p, --server-port Set the destination port for outgoing SPA\n"
|
|
" packet.\n"
|
|
" -P, --server-proto Set the protocol (udp, tcp, http, tcpraw,\n"
|
|
" icmp) for the outgoing SPA packet.\n"
|
|
" Note: The 'tcpraw' and 'icmp' modes use raw\n"
|
|
" sockets and thus require root access to use.\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 a URL such as the default of:\n"
|
|
" http://" HTTP_RESOLVE_HOST HTTP_RESOLVE_URL "\n"
|
|
" This can be overridden with the --resolve-url\n"
|
|
" option.\n"
|
|
" --resolve-url Override the default URL used for resolving\n"
|
|
" the source IP address.\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"
|
|
" -H, --http-proxy Specify an HTTP proxy host through which the\n"
|
|
" SPA packet will be sent. The port can also be\n"
|
|
" specified here by following the host/ip with\n"
|
|
" \":<port>\".\n"
|
|
" -U, --spoof-user Set the username within outgoing SPA packet.\n"
|
|
" -l, --last-cmd Run the fwknop client with the same command\n"
|
|
" line args as the last time it was executed\n"
|
|
" (args are read from the ~/.fwknop.run file).\n"
|
|
" -G, --get-key Load an encryption key/password from a file.\n"
|
|
" --stdin Read the encryption key/password from stdin\n"
|
|
" --fd Specify the file descriptor to read the\n"
|
|
" encryption key/password from.\n"
|
|
" -k, --key-gen Generate SPA Rijndael + HMAC keys.\n"
|
|
" -K, --key-gen-file Write generated Rijndael + HMAC keys to a\n"
|
|
" file\n"
|
|
" --key-rijndael Specify the Rijndael key. Since the password is\n"
|
|
" visible to utilities (like 'ps' under Unix) this\n"
|
|
" form should only be used where security is not\n"
|
|
" important.\n"
|
|
" --key-base64-rijndael Specify the base64 encoded Rijndael key. Since\n"
|
|
" the password is visible to utilities (like 'ps'\n"
|
|
" under Unix) this form should only be used where\n"
|
|
" security is not important.\n"
|
|
" --key-base64-hmac Specify the base64 encoded HMAC key. Since the\n"
|
|
" password is visible to utilities (like 'ps'\n"
|
|
" under Unix) this form should only be used where\n"
|
|
" security is not important.\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 (may specify multiple times).\n"
|
|
" -V, --version Print version number.\n"
|
|
" -m, --digest-type Specify the message digest algorithm to use.\n"
|
|
" (md5, sha1, sha256, sha384, or sha512). The\n"
|
|
" default is sha256.\n"
|
|
" -M, --encryption-mode Specify the encryption mode when AES is used\n"
|
|
" for encrypting SPA packets.The default is CBC\n"
|
|
" mode, but others can be chosen such as CFB or\n"
|
|
" OFB as long as this is also specified in the\n"
|
|
" access.conf file on the server side. Note that\n"
|
|
" the string ``legacy'' can be specified in order\n"
|
|
" to generate SPA packets with the old initialization\n"
|
|
" vector strategy used by versions of *fwknop*\n"
|
|
" before 2.5.\n"
|
|
" -f, --fw-timeout Specify SPA server firewall timeout from the\n"
|
|
" client side.\n"
|
|
" --hmac-digest-type Set the HMAC digest algorithm (default is\n"
|
|
" sha256). Options are md5, sha1, sha256,\n"
|
|
" sha384, or sha512.\n"
|
|
" --icmp-type Set the ICMP type (used with '-P icmp')\n"
|
|
" --icmp-code Set the ICMP code (used with '-P icmp')\n"
|
|
" --gpg-encryption Use GPG encryption (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"
|
|
" --no-save-args Do not save fwknop command line args to the\n"
|
|
" $HOME/fwknop.run file\n"
|
|
" --rc-file Specify path to the fwknop rc file (default\n"
|
|
" is $HOME/.fwknoprc)\n"
|
|
" --save-rc-stanza Save command line arguments to the\n"
|
|
" $HOME/.fwknoprc stanza specified with the\n"
|
|
" -n option.\n"
|
|
" --force-stanza Used with --save-rc-stanza to overwrite all of\n"
|
|
" the variables for the specified stanza\n"
|
|
" --stanza-list Dump a list of the stanzas found in\n"
|
|
" $HOME/.fwknoprc\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"
|
|
);
|
|
|
|
return;
|
|
}
|
|
|