* Got forward and local NAT modes working with the --nat-access,

--nat-local, --nat-port, and --nat-randport options.  All NAT modes
  are now passing the fwknop test suite.
* Added the --server-command option to build an SPA packet with a command
  for the server to execute.
* Added the --fw-timeout option for client side timeouts to be specified.
* Added the --time-offset-plus and --time-offset-minus options to allow
  the user to influence the timestamp associated with an SPA packet.
* Added the --rand-port option so that the SPA packet destination port can
  be randomized.


git-svn-id: file:///home/mbr/svn/fwknop/trunk@115 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
Michael Rash 2009-07-21 05:18:28 +00:00
parent 3a77e0fefa
commit 70db22064e
6 changed files with 425 additions and 38 deletions

View File

@ -1,11 +1,23 @@
2009-07-21 Michael Rash <mbr@cipherdyne.org>
* Got forward and local NAT modes working with the --nat-access,
--nat-local, --nat-port, and --nat-randport options. All NAT modes
are now passing the fwknop test suite.
* Added the --server-command option to build an SPA packet with a command
for the server to execute.
* Added the --fw-timeout option for client side timeouts to be specified.
* Added the --time-offset-plus and --time-offset-minus options to allow
the user to influence the timestamp associated with an SPA packet.
* Added the --rand-port option so that the SPA packet destination port can
be randomized.
2009-07-16 Michael Rash <mbr@cipherdyne.org>
* Added the ability to send SPA packets over valid HTTP requests with
* Added the ability to send SPA packets over valid HTTP requests with
the fwknop-c client.
* Added support for transmitting SPA packets over IPv6 via TCP and UDP
* Added support for transmitting SPA packets over IPv6 via TCP and UDP
sockets, and also via HTTP.
* Added GnuPG 'hQ' base64 encoded prefix handling (this prefix is
* Added GnuPG 'hQ' base64 encoded prefix handling (this prefix is
stripped out of encrypted SPA packet data).
* Added hostname resolution support to the fwknop-c client if the SPA
* Added hostname resolution support to the fwknop-c client if the SPA
server is specified as a hostname instead of an IP address.
2008-12-21 Damien Stuart <dstuart@dstuart.org>

View File

@ -73,6 +73,56 @@ get_char_val(const char *var_name, char *dest, char *lptr)
return 1;
}
/* Parse any time offset from the command line
*/
static int
parse_time_offset(char *offset_str)
{
int offset = 0, i, j, offset_type = TIME_OFFSET_SECONDS;
char offset_digits[MAX_TIME_STR_LEN];
j=0;
for (i=0; i < strlen(offset_str); 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
@ -97,7 +147,7 @@ parse_config_file(fko_cli_options_t *options, struct opts_track* ot)
{
fprintf(stderr, "[*] Could not open config file: %s\n",
options->config_file);
exit(1);
exit(EXIT_FAILURE);
}
fprintf(stderr,
@ -106,7 +156,7 @@ parse_config_file(fko_cli_options_t *options, struct opts_track* ot)
return;
}
if ((cfile_ptr = fopen(options->config_file, "r")) == NULL)
{
fprintf(stderr, "[*] Could not open config file: %s\n",
@ -159,7 +209,7 @@ validate_options(fko_cli_options_t *options)
{
fprintf(stderr,
"[*] Must use --destination unless --test mode is used\n");
exit(1);
exit(EXIT_FAILURE);
}
/* If we are using gpg, we must at least have the recipient set.
@ -171,7 +221,7 @@ validate_options(fko_cli_options_t *options)
{
fprintf(stderr,
"[*] Must specify --gpg-recipient-key when GPG is used.\n");
exit(1);
exit(EXIT_FAILURE);
}
}
@ -184,9 +234,7 @@ validate_options(fko_cli_options_t *options)
void
config_init(fko_cli_options_t *options, int argc, char **argv)
{
int cmd_arg, index;
//unsigned int tmpint;
int cmd_arg, index, i;
struct opts_track ot;
/* Zero out options and opts_track.
@ -199,10 +247,10 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
*/
options->spa_proto = FKO_DEFAULT_PROTO;
options->spa_dst_port = FKO_DEFAULT_PORT;
strlcpy(options->spa_dst_port_str, FKO_DEFAULT_PORT_STR, MAX_PORT_STR_LEN);
options->fw_timeout = -1;
while ((cmd_arg = getopt_long(argc, argv,
"a:A:bB:D:gG:hm:np:P:qQ:S:TU:vV", cmd_opts, &index)) != -1) {
"a:A:bB:C:D:f:gG:hIm:nN:p:P:qQ:rS:TU:vV", cmd_opts, &index)) != -1) {
switch(cmd_arg) {
case 'a':
@ -217,9 +265,19 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
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;
@ -229,7 +287,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
break;
case 'h':
usage();
exit(0);
exit(EXIT_SUCCESS);
case 'm':
case FKO_DIGEST_NAME:
if(strncasecmp(optarg, "md5", 3) == 0)
@ -241,18 +299,20 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
else
{
fprintf(stderr, "* Invalid digest type: %s\n", optarg);
exit(1);
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':
strlcpy(options->spa_dst_port_str, optarg, MAX_PORT_STR_LEN);
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(1);
exit(EXIT_FAILURE);
}
break;
case 'P':
@ -268,7 +328,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
options->spa_proto = FKO_PROTO_HTTP;
else {
fprintf(stderr, "[*] Unrecognized protocol: %s\n", optarg);
exit(1);
exit(EXIT_FAILURE);
}
break;
case 'q':
@ -277,12 +337,14 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
case 'Q':
strlcpy(options->spoof_ip_src_str, optarg, MAX_IP_STR_LEN);
break;
case 'r':
options->rand_port = 1;
break;
case 'S':
strlcpy(options->spa_src_port_str, optarg, MAX_PORT_STR_LEN);
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(1);
exit(EXIT_FAILURE);
}
break;
case 'T':
@ -313,9 +375,28 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
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;
default:
usage();
exit(1);
exit(EXIT_FAILURE);
}
}
@ -349,6 +430,8 @@ usage(void)
" -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"
@ -360,17 +443,29 @@ usage(void)
" -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 - Use GPG agent if available.\n"
" --nat-port - Use GPG agent if available.\n"
" --nat-rand-port - Use GPG agent if available.\n"
" --nat-local - Use GPG agent if available.\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"
);

View File

@ -33,6 +33,11 @@
*/
enum {
FKO_DIGEST_NAME = 0x100,
NAT_LOCAL,
NAT_PORT,
NAT_RAND_PORT,
TIME_OFFSET_MINUS,
TIME_OFFSET_PLUS,
/* Put GPG-related items below the following line */
GPG_ENCRYPTION = 0x200,
GPG_RECIP_KEY,
@ -50,8 +55,10 @@ static struct option cmd_opts[] =
{"access", 1, NULL, 'A'},
{"save-packet-append", 0, NULL, 'b'},
{"save-packet", 1, NULL, 'B'},
{"server-command", 1, NULL, 'C'},
{"digest-type", 1, NULL, FKO_DIGEST_NAME},
{"destination", 1, NULL, 'D'},
{"fw-timeout", 1, NULL, 'f'},
{"gpg-encryption", 0, NULL, 'g'},
{"gpg-recipient-key", 1, NULL, GPG_RECIP_KEY },
{"gpg-signer-key", 1, NULL, GPG_SIGNER_KEY },
@ -60,12 +67,19 @@ static struct option cmd_opts[] =
{"get-key", 1, NULL, 'G'},
{"help", 0, NULL, 'h'},
{"no-save", 0, NULL, 'n'},
{"nat-access", 1, NULL, 'N'},
{"nat-local", 0, NULL, NAT_LOCAL},
{"nat-port", 1, NULL, NAT_PORT},
{"nat-rand-port", 0, NULL, NAT_RAND_PORT},
{"server-port", 1, NULL, 'p'},
{"server-proto", 1, NULL, 'P'},
{"quiet", 0, NULL, 'q'},
{"rand-port", 0, NULL, 'r'},
{"spoof-src", 1, NULL, 'Q'},
{"source-port", 1, NULL, 'S'},
{"test", 0, NULL, 'T'},
{"time-offset-plus", 1, NULL, TIME_OFFSET_PLUS},
{"time-offset-minus", 1, NULL, TIME_OFFSET_MINUS},
{"spoof-user", 1, NULL, 'U'},
{"verbose", 0, NULL, 'v'},
{"version", 0, NULL, 'V'},

View File

@ -33,7 +33,11 @@
*/
char* get_user_pw(fko_cli_options_t *options, int crypt_op);
static void display_ctx(fko_ctx_t ctx);
void errmsg(char *msg, int err);
void errmsg(char *msg, int err);
static int set_message_type(fko_ctx_t ctx, fko_cli_options_t *options);
static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options);
static int get_rand_port(fko_ctx_t ctx);
static void dump_transmit_options(fko_cli_options_t *options);
int
main(int argc, char **argv)
@ -69,6 +73,84 @@ main(int argc, char **argv)
return(0);
}
/* Set client timeout
*/
if(options.fw_timeout >= 0)
{
res = fko_set_spa_client_timeout(ctx, options.fw_timeout);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_spa_client_timeout", res);
return(1);
}
}
/* Set the SPA packet message type based on command line options
*/
res = set_message_type(ctx, &options);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_spa_message_type", res);
return(1);
}
if(options.server_command[0] != 0x0)
{
/* Set the access message to a command that the server will
* execute
*/
snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
options.allow_ip_str, ",", options.server_command);
}
else
{
/* Set a message string by combining the allow IP and the
* port/protocol. The fwknopd server allows no port/protocol
* to be specified as well, so in this case append the string
* "none/0" to the allow IP.
*/
if(options.access_str[0] != 0x0)
{
snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
options.allow_ip_str, ",", options.access_str);
}
else
{
snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
options.allow_ip_str, ",", "none/0");
}
}
res = fko_set_spa_message(ctx, access_buf);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_spa_message", res);
return(1);
}
/* Set NAT access string
*/
if (options.nat_local || options.nat_access_str[0] != 0x0)
{
res = set_nat_access(ctx, &options);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_nat_access_str", res);
return(1);
}
}
/* Set username
*/
if(options.spoof_user[0] != 0x0)
{
res = fko_set_username(ctx, options.spoof_user);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_username", res);
return(1);
}
}
/* Set up for using GPG if specified.
*/
if(options.use_gpg)
@ -127,17 +209,6 @@ main(int argc, char **argv)
}
}
/* Set a message string by combining the allow IP and the port/protocol
*/
snprintf(access_buf, MAX_LINE_LEN, "%s%s%s",
options.allow_ip_str, ",", options.access_str);
res = fko_set_spa_message(ctx, access_buf);
if(res != FKO_SUCCESS)
{
errmsg("fko_set_spa_message", res);
return(1);
}
/* Set Digest type.
*/
if(options.digest_type)
@ -173,6 +244,12 @@ main(int argc, char **argv)
if (options.save_packet_file[0] != 0x0)
write_spa_packet_data(ctx, &options);
if (options.rand_port)
options.spa_dst_port = get_rand_port(ctx);
if (options.verbose)
dump_transmit_options(&options);
/* If not in test mode, send the SPA data across the wire with a
* protocol/port specified on the command line (default is UDP/62201).
* Otherwise, run through a decode cycle (--DSS XXX: This test/decode
@ -270,6 +347,168 @@ main(int argc, char **argv)
return(0);
}
static void
print_proto(int proto)
{
switch (proto) {
case FKO_PROTO_UDP:
printf("udp");
break;
case FKO_PROTO_TCP_RAW:
printf("tcpraw");
break;
case FKO_PROTO_TCP:
printf("tcp");
break;
case FKO_PROTO_ICMP:
printf("icmp");
break;
case FKO_PROTO_HTTP:
printf("http");
break;
}
return;
}
static
int get_rand_port(fko_ctx_t ctx)
{
char *rand_val = NULL;
int port = 0;
int res = 0;
res = fko_get_rand_value(ctx, &rand_val);
if(res != FKO_SUCCESS)
{
errmsg("get_rand_port(), fko_get_rand_value", res);
exit(EXIT_FAILURE);
}
/* convert to a random value between 1024 and 65535
*/
return (MIN_HIGH_PORT + (atoi(rand_val) % (MAX_PORT - MIN_HIGH_PORT)));
}
static void
dump_transmit_options(fko_cli_options_t *options)
{
printf("[+] Generating SPA packet:\n protocol: ");
print_proto(options->spa_proto),
printf("\n port: %d\n", options->spa_dst_port);
return;
}
/* See if the string is of the format "<ipv4 addr>:<port>",
* e.g. "123.1.2.3,12345" - this needs work.
*/
static int ipv4_str_has_port(char *str)
{
int rv = 0, i;
for (i=0; i < strlen(str); i++) {
if (str[i] == ',' || str[i] == ':') {
str[i] = ','; /* force "<ip>,<port>" format */
rv = 1;
continue;
}
if (rv && ! isdigit(str[i])) {
rv = 0;
break;
}
}
return rv;
}
/* Set NAT access string
*/
static int
set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options)
{
char nat_access_buf[MAX_LINE_LEN] = "";
int nat_port = 0;
if (options->nat_rand_port)
nat_port = get_rand_port(ctx);
else if (options->nat_port)
nat_port = options->nat_port;
else
nat_port = DEFAULT_NAT_PORT;
if (options->nat_local && options->nat_access_str[0] == 0x0)
{
snprintf(nat_access_buf, MAX_LINE_LEN, "%s,%d",
options->spa_server_str, nat_port);
}
if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0)
{
if (ipv4_str_has_port(options->nat_access_str))
{
snprintf(nat_access_buf, MAX_LINE_LEN, "%s",
options->nat_access_str);
}
else
{
snprintf(nat_access_buf, MAX_LINE_LEN, "%s,%d",
options->nat_access_str, nat_port);
}
}
return fko_set_spa_nat_access(ctx, nat_access_buf);
}
/* Set the SPA packet message type
*/
static int
set_message_type(fko_ctx_t ctx, fko_cli_options_t *options)
{
short message_type;
if(options->server_command[0] != 0x0)
{
message_type = FKO_COMMAND_MSG;
}
else if(options->nat_access_str[0] != 0x0)
{
if (options->nat_local)
{
if (options->fw_timeout >= 0)
{
message_type = FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG;
}
else
{
message_type = FKO_LOCAL_NAT_ACCESS_MSG;
}
}
else
{
if (options->fw_timeout >= 0)
{
message_type = FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG;
}
else
{
message_type = FKO_NAT_ACCESS_MSG;
}
}
}
else
{
if (options->fw_timeout >= 0)
{
message_type = FKO_CLIENT_TIMEOUT_ACCESS_MSG;
}
else
{
message_type = FKO_ACCESS_MSG;
}
}
printf("....setting message type to: %d\n", message_type);
return fko_set_spa_message_type(ctx, message_type);
}
/* Prompt for and receive a user password.
*/
char*

View File

@ -91,8 +91,11 @@ enum {
*/
#define FKO_DEFAULT_PROTO FKO_PROTO_UDP
#define FKO_DEFAULT_PORT 62201
#define FKO_DEFAULT_PORT_STR "62201"
#define DEFAULT_NAT_PORT 55000
#define MIN_HIGH_PORT 1024
#define MAX_PORT 65535
#define MAX_PORT_STR_LEN 6
#define MAX_PROTO_STR_LEN 6
#define MAX_IP_STR_LEN 16
#define MAX_SERVER_STR_LEN 50
@ -101,12 +104,23 @@ enum {
#define MAX_GPG_KEY_ID 128
#define MAX_USERNAME_LEN 30
#define MAX_TIME_STR_LEN 9
enum {
TIME_OFFSET_SECONDS,
TIME_OFFSET_MINUTES,
TIME_OFFSET_HOURS,
TIME_OFFSET_DAYS
};
#define RAND_FILE "/dev/urandom"
/* fwkop client configuration parameters and values
*/
typedef struct fko_cli_options
{
char config_file[MAX_PATH_LEN];
char access_str[MAX_PATH_LEN];
char server_command[MAX_LINE_LEN];
char get_key_file[MAX_LINE_LEN];
char save_packet_file[MAX_LINE_LEN];
int save_packet_file_append;
@ -114,15 +128,23 @@ typedef struct fko_cli_options
char allow_ip_str[MAX_IP_STR_LEN];
char spoof_ip_src_str[MAX_IP_STR_LEN];
char spoof_user[MAX_USERNAME_LEN];
int rand_port;
char gpg_recipient_key[MAX_GPG_KEY_ID];
char gpg_signer_key[MAX_GPG_KEY_ID];
char gpg_home_dir[MAX_PATH_LEN];
/* NAT access
*/
char nat_access_str[MAX_PATH_LEN];
int nat_local;
int nat_port;
int nat_rand_port;
/* SPA packet transmission port and protocol
*/
int spa_proto;
unsigned int spa_dst_port;
char spa_dst_port_str[MAX_PORT_STR_LEN];
unsigned int spa_src_port; /* only used with --source-port */
char spa_src_port_str[MAX_PORT_STR_LEN]; /* --source-port */
unsigned int digest_type;
@ -134,6 +156,9 @@ typedef struct fko_cli_options
unsigned char test;
unsigned char use_gpg;
unsigned char use_gpg_agent;
int time_offset_plus;
int time_offset_minus;
int fw_timeout;
//char config_file[MAX_PATH_LEN];

View File

@ -76,6 +76,7 @@ send_spa_packet_tcp_or_udp(char *spa_data, int sd_len, fko_cli_options_t *option
{
int sock, res, error;
struct addrinfo *result, *rp, hints;
char port_str[MAX_PORT_STR_LEN];
memset(&hints, 0, sizeof(struct addrinfo));
@ -97,8 +98,9 @@ send_spa_packet_tcp_or_udp(char *spa_data, int sd_len, fko_cli_options_t *option
hints.ai_protocol = IPPROTO_TCP;
}
error = getaddrinfo(options->spa_server_str,
options->spa_dst_port_str, &hints, &result);
sprintf(port_str, "%d", options->spa_dst_port);
error = getaddrinfo(options->spa_server_str, port_str, &hints, &result);
if (error != 0)
{