Major rearrangement. Renamed directories: "fko" to "lib", "src" to "client". Added "common" and "server" directories. Setup autoconf to allow disabling the server and/or client builds.
git-svn-id: file:///home/mbr/svn/fwknop/trunk@127 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
8
client/Makefile.am
Normal file
8
client/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
bin_PROGRAMS = fwknop
|
||||
|
||||
fwknop_SOURCES = fwknop.c fwknop.h config_init.c config_init.h \
|
||||
fwknop_common.h spa_comm.c spa_comm.h utils.c utils.h \
|
||||
http_resolve_host.c getpasswd.c getpasswd.h
|
||||
|
||||
fwknop_LDADD = $(top_builddir)/lib/libfko.la
|
||||
fwknop_CPPFLAGS = -I $(top_srcdir)/lib -I $(top_srcdir)/common
|
||||
527
client/config_init.c
Normal file
527
client/config_init.c
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
*
|
||||
* File: config_init.c
|
||||
*
|
||||
* Author: Damien Stuart
|
||||
*
|
||||
* Purpose: Command-line and config file processing for fwknop client.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#include "fwknop_common.h"
|
||||
#include "config_init.h"
|
||||
#include "getopt.h"
|
||||
#include "utils.h"
|
||||
#include "ctype.h"
|
||||
|
||||
/* Routine to extract the configuration value from a line in the config
|
||||
* file.
|
||||
*/
|
||||
int
|
||||
get_char_val(const char *var_name, char *dest, char *lptr)
|
||||
{
|
||||
int i, var_char_ctr = 0;
|
||||
char *tmp_ptr;
|
||||
|
||||
tmp_ptr = lptr;
|
||||
|
||||
/* var_name is guaranteed to be NULL-terminated.
|
||||
*/
|
||||
for (i=0; i < (int)strlen(var_name); i++)
|
||||
if (tmp_ptr[i] != var_name[i])
|
||||
return 0;
|
||||
|
||||
tmp_ptr += i;
|
||||
|
||||
/* First char after varName better be a space or tab or '='.
|
||||
*/
|
||||
if (*tmp_ptr != ' ' && *tmp_ptr != '\t' && *tmp_ptr != '=')
|
||||
return 0;
|
||||
|
||||
/* Walk past the delimiter.
|
||||
*/
|
||||
while (*tmp_ptr == ' ' || *tmp_ptr == '\t' || *tmp_ptr == '=')
|
||||
tmp_ptr++;
|
||||
|
||||
while (var_char_ctr < MAX_LINE_LEN && tmp_ptr[var_char_ctr] != '\n'
|
||||
&& tmp_ptr[var_char_ctr] != '\0')
|
||||
var_char_ctr++;
|
||||
|
||||
if (tmp_ptr[var_char_ctr] != '\n' || var_char_ctr >= MAX_LINE_LEN)
|
||||
return 0;
|
||||
|
||||
strncpy(dest, tmp_ptr, var_char_ctr);
|
||||
|
||||
dest[var_char_ctr] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse any time offset from the command line
|
||||
*/
|
||||
static int
|
||||
parse_time_offset(char *offset_str)
|
||||
{
|
||||
int i, j;
|
||||
int offset = 0;
|
||||
int offset_type = TIME_OFFSET_SECONDS;
|
||||
int os_len = strlen(offset_str);
|
||||
|
||||
char offset_digits[MAX_TIME_STR_LEN];
|
||||
|
||||
j=0;
|
||||
for (i=0; i < os_len; i++) {
|
||||
if (isdigit(offset_str[i])) {
|
||||
offset_digits[j] = offset_str[i];
|
||||
j++;
|
||||
} else if (offset_str[i] == 'm' || offset_str[i] == 'M') {
|
||||
offset_type = TIME_OFFSET_MINUTES;
|
||||
break;
|
||||
} else if (offset_str[i] == 'h' || offset_str[i] == 'H') {
|
||||
offset_type = TIME_OFFSET_HOURS;
|
||||
break;
|
||||
} else if (offset_str[i] == 'd' || offset_str[i] == 'D') {
|
||||
offset_type = TIME_OFFSET_DAYS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset_digits[j] = '\0';
|
||||
if (j < 1) {
|
||||
fprintf(stderr, "[*] Invalid time offset: %s", offset_str);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
offset = atoi(offset_digits);
|
||||
if (offset < 0) {
|
||||
fprintf(stderr, "[*] Invalid time offset: %s", offset_str);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
switch (offset_type) {
|
||||
case TIME_OFFSET_MINUTES:
|
||||
offset *= 60;
|
||||
break;
|
||||
case TIME_OFFSET_HOURS:
|
||||
offset *= 60 * 60;
|
||||
break;
|
||||
case TIME_OFFSET_DAYS:
|
||||
offset *= 60 * 60 * 24;
|
||||
break;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Parse the config file...
|
||||
*/
|
||||
static void
|
||||
parse_config_file(fko_cli_options_t *options, struct opts_track* ot)
|
||||
{
|
||||
FILE *cfile_ptr;
|
||||
unsigned int numLines = 0;
|
||||
|
||||
char conf_line_buf[MAX_LINE_LEN] = {0};
|
||||
char tmp_char_buf[MAX_LINE_LEN] = {0};
|
||||
char *lptr;
|
||||
|
||||
struct stat st;
|
||||
|
||||
/* First see if the config file exists. If it doesn't, and was
|
||||
* specified via command-line, then error out. Otherwise, complain
|
||||
* and go on with program defaults.
|
||||
*/
|
||||
if(stat(options->config_file, &st) != 0)
|
||||
{
|
||||
if(ot->got_config_file)
|
||||
{
|
||||
fprintf(stderr, "[*] Could not open config file: %s\n",
|
||||
options->config_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"** Config file was not found. Attempting to continue with defaults...\n"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cfile_ptr = fopen(options->config_file, "r")) == NULL)
|
||||
{
|
||||
fprintf(stderr, "[*] Could not open config file: %s\n",
|
||||
options->config_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while ((fgets(conf_line_buf, MAX_LINE_LEN, cfile_ptr)) != NULL)
|
||||
{
|
||||
numLines++;
|
||||
conf_line_buf[MAX_LINE_LEN-1] = '\0';
|
||||
lptr = conf_line_buf;
|
||||
|
||||
memset(tmp_char_buf, 0x0, MAX_LINE_LEN);
|
||||
|
||||
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
|
||||
lptr++;
|
||||
|
||||
/* Get past comments and empty lines.
|
||||
*/
|
||||
if (*lptr == '#' || *lptr == '\n' || *lptr == '\r' || *lptr == '\0' || *lptr == ';')
|
||||
continue;
|
||||
|
||||
/*--DSS TODO: Figure out what to put here
|
||||
|
||||
if (ot->got_device == 0 || options->interface.name[0] == '\0')
|
||||
get_char_val("XXXX", options->interface.name, lptr);
|
||||
|
||||
|
||||
if (ot->got_snaplen == 0 && get_char_val("SNAPLEN", tmp_char_buf, lptr))
|
||||
options->snapLen = atoi(tmp_char_buf);
|
||||
*/
|
||||
}
|
||||
|
||||
fclose(cfile_ptr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sanity and bounds checks for the various options.
|
||||
*/
|
||||
static void
|
||||
validate_options(fko_cli_options_t *options)
|
||||
{
|
||||
/* Gotta have a Destination unless we are just testing or getting the
|
||||
* the version, and must use one of [-s|-R|-a].
|
||||
*/
|
||||
if(!options->test && !options->version && !options->show_last_command)
|
||||
{
|
||||
if (options->spa_server_str[0] == 0x0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[*] Must use --destination unless --test mode is used\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!options->resolve_ip_http && options->allow_ip_str[0] == 0x0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[*] Must use one of [-s|-R|-a] to specify IP for SPA access.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(options->resolve_ip_http || options->spa_proto == FKO_PROTO_HTTP)
|
||||
if (options->http_user_agent[0] == '\0')
|
||||
snprintf(options->http_user_agent, HTTP_MAX_USER_AGENT_LEN,
|
||||
"%s%s", "Fwknop/", MY_VERSION);
|
||||
|
||||
/* If we are using gpg, we must at least have the recipient set.
|
||||
*/
|
||||
if(options->use_gpg)
|
||||
{
|
||||
if(options->gpg_recipient_key == NULL
|
||||
|| strlen(options->gpg_recipient_key) == 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[*] Must specify --gpg-recipient-key when GPG is used.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize program configuration via config file and/or command-line
|
||||
* switches.
|
||||
*/
|
||||
void
|
||||
config_init(fko_cli_options_t *options, int argc, char **argv)
|
||||
{
|
||||
int cmd_arg, index;
|
||||
struct opts_track ot;
|
||||
|
||||
/* Zero out options and opts_track.
|
||||
*/
|
||||
memset(options, 0x00, sizeof(fko_cli_options_t));
|
||||
memset(&ot, 0x00, sizeof(ot));
|
||||
|
||||
/* Establish a few defaults such as UDP/62201 for sending the SPA
|
||||
* packet (can be changed with --server-proto/--server-port)
|
||||
*/
|
||||
options->spa_proto = FKO_DEFAULT_PROTO;
|
||||
options->spa_dst_port = FKO_DEFAULT_PORT;
|
||||
options->fw_timeout = -1;
|
||||
|
||||
while ((cmd_arg = getopt_long(argc, argv,
|
||||
"a:A:bB:C:D:f:gG:hIm:nN:p:P:qQ:rRsS:Tu:U:vV", cmd_opts, &index)) != -1) {
|
||||
|
||||
switch(cmd_arg) {
|
||||
case 'a':
|
||||
strlcpy(options->allow_ip_str, optarg, MAX_IP_STR_LEN);
|
||||
break;
|
||||
case 'A':
|
||||
strlcpy(options->access_str, optarg, MAX_LINE_LEN);
|
||||
break;
|
||||
case 'b':
|
||||
options->save_packet_file_append = 1;
|
||||
break;
|
||||
case 'B':
|
||||
strlcpy(options->save_packet_file, optarg, MAX_PATH_LEN);
|
||||
break;
|
||||
case 'C':
|
||||
strlcpy(options->server_command, optarg, MAX_LINE_LEN);
|
||||
break;
|
||||
case 'D':
|
||||
strlcpy(options->spa_server_str, optarg, MAX_SERVER_STR_LEN);
|
||||
break;
|
||||
case 'f':
|
||||
options->fw_timeout = atoi(optarg);
|
||||
if (options->fw_timeout < 0) {
|
||||
fprintf(stderr, "[*] --fw-timeout must be >= 0\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
case GPG_ENCRYPTION:
|
||||
options->use_gpg = 1;
|
||||
break;
|
||||
case 'G':
|
||||
strlcpy(options->get_key_file, optarg, MAX_PATH_LEN);
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'm':
|
||||
case FKO_DIGEST_NAME:
|
||||
if(strncasecmp(optarg, "md5", 3) == 0)
|
||||
options->digest_type = FKO_DIGEST_MD5;
|
||||
else if(strncasecmp(optarg, "sha1", 4) == 0)
|
||||
options->digest_type = FKO_DIGEST_SHA1;
|
||||
else if(strncasecmp(optarg, "sha256", 6) == 0)
|
||||
options->digest_type = FKO_DIGEST_SHA256;
|
||||
else if(strncasecmp(optarg, "sha384", 6) == 0)
|
||||
options->digest_type = FKO_DIGEST_SHA384;
|
||||
else if(strncasecmp(optarg, "sha512", 6) == 0)
|
||||
options->digest_type = FKO_DIGEST_SHA512;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "* Invalid digest type: %s\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
options->no_save = 1;
|
||||
break;
|
||||
case 'N':
|
||||
strlcpy(options->nat_access_str, optarg, MAX_LINE_LEN);
|
||||
break;
|
||||
case 'p':
|
||||
options->spa_dst_port = atoi(optarg);
|
||||
if (options->spa_dst_port < 0 || options->spa_dst_port > 65535) {
|
||||
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
if (strncmp(optarg, "udp", strlen("udp")) == 0)
|
||||
options->spa_proto = FKO_PROTO_UDP;
|
||||
else if (strncmp(optarg, "tcpraw", strlen("tcpraw")) == 0)
|
||||
options->spa_proto = FKO_PROTO_TCP_RAW;
|
||||
else if (strncmp(optarg, "tcp", strlen("tcp")) == 0)
|
||||
options->spa_proto = FKO_PROTO_TCP;
|
||||
else if (strncmp(optarg, "icmp", strlen("icmp")) == 0)
|
||||
options->spa_proto = FKO_PROTO_ICMP;
|
||||
else if (strncmp(optarg, "http", strlen("http")) == 0)
|
||||
options->spa_proto = FKO_PROTO_HTTP;
|
||||
else {
|
||||
fprintf(stderr, "[*] Unrecognized protocol: %s\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
options->quiet = 1;
|
||||
break;
|
||||
case 'Q':
|
||||
strlcpy(options->spoof_ip_src_str, optarg, MAX_IP_STR_LEN);
|
||||
break;
|
||||
case 'r':
|
||||
options->rand_port = 1;
|
||||
break;
|
||||
case 'R':
|
||||
options->resolve_ip_http = 1;
|
||||
break;
|
||||
case SHOW_LAST_ARGS:
|
||||
options->show_last_command = 1;
|
||||
break;
|
||||
case 's':
|
||||
strlcpy(options->allow_ip_str, "0.0.0.0", MAX_IP_STR_LEN);
|
||||
break;
|
||||
case 'S':
|
||||
options->spa_src_port = atoi(optarg);
|
||||
if (options->spa_src_port < 0 || options->spa_src_port > 65535) {
|
||||
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
options->test = 1;
|
||||
break;
|
||||
case 'u':
|
||||
strlcpy(options->http_user_agent, optarg, HTTP_MAX_USER_AGENT_LEN);
|
||||
break;
|
||||
case 'U':
|
||||
strlcpy(options->spoof_user, optarg, MAX_USERNAME_LEN);
|
||||
break;
|
||||
case 'v':
|
||||
options->verbose = 1;
|
||||
break;
|
||||
case 'V':
|
||||
options->version = 1;
|
||||
break;
|
||||
case GPG_RECIP_KEY:
|
||||
options->use_gpg = 1;
|
||||
strlcpy(options->gpg_recipient_key, optarg, MAX_GPG_KEY_ID);
|
||||
break;
|
||||
case GPG_SIGNER_KEY:
|
||||
options->use_gpg = 1;
|
||||
strlcpy(options->gpg_signer_key, optarg, MAX_GPG_KEY_ID);
|
||||
break;
|
||||
case GPG_HOME_DIR:
|
||||
options->use_gpg = 1;
|
||||
strlcpy(options->gpg_home_dir, optarg, MAX_PATH_LEN);
|
||||
break;
|
||||
case GPG_AGENT:
|
||||
options->use_gpg = 1;
|
||||
options->use_gpg_agent = 1;
|
||||
break;
|
||||
case NAT_LOCAL:
|
||||
options->nat_local = 1;
|
||||
break;
|
||||
case NAT_RAND_PORT:
|
||||
options->nat_rand_port = 1;
|
||||
break;
|
||||
case NAT_PORT:
|
||||
options->nat_port = atoi(optarg);
|
||||
if (options->nat_port < 0 || options->nat_port > 65535) {
|
||||
fprintf(stderr, "[*] Unrecognized port: %s\n", optarg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case TIME_OFFSET_PLUS:
|
||||
options->time_offset_plus = parse_time_offset(optarg);
|
||||
break;
|
||||
case TIME_OFFSET_MINUS:
|
||||
options->time_offset_minus = parse_time_offset(optarg);
|
||||
break;
|
||||
case NO_SAVE_ARGS:
|
||||
options->no_save_args = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse configuration file to populate any params not already specified
|
||||
* via command-line options
|
||||
*/
|
||||
//--DSS XXX: We will use this when we have a config file to use.
|
||||
//parse_config_file(options, &ot);
|
||||
|
||||
/* Now that we have all of our options set, we can validate them.
|
||||
*/
|
||||
validate_options(options);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print usage message...
|
||||
*/
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "\n%s client version %s\n%s\n\n", MY_NAME, MY_VERSION, MY_DESC);
|
||||
fprintf(stderr,
|
||||
"Usage: fwknop -A <port list> [-s|-R|-a] -D <spa_server> [options]\n\n"
|
||||
" -h, --help - Print this usage message and exit.\n"
|
||||
" -c, --config-file - Specify an alternate configuration file.\n"
|
||||
" -A, --access - Provide a list of ports/protocols to open\n"
|
||||
" on the server.\n"
|
||||
" -B, --save-packet - Save the generated packet data to the\n"
|
||||
" specified file.\n"
|
||||
" -a, --allow-ip - Specify IP address to allow within the SPA\n"
|
||||
" packet.\n"
|
||||
" -D, --destination - Specify the IP address of the fwknop server.\n"
|
||||
" -N, --nat-access - Gain NAT access to an internal service\n"
|
||||
" protected by the fwknop server.\n"
|
||||
" -p, --server-port - Set the destination port for outgoing SPA\n"
|
||||
" packet.\n"
|
||||
" -P, --server-proto - Set the protocol (udp, tcp, tcpraw, icmp) for\n"
|
||||
" the outgoing SPA packet. Note: The 'tcpraw'\n"
|
||||
" and 'icmp' modes use raw sockets and thus\n"
|
||||
" require root access to run.\n"
|
||||
" -s, --source-ip - Tell the fwknopd server to accept whatever\n"
|
||||
" source IP the SPA packet has as the IP that\n"
|
||||
" needs access (not recommended, and the\n"
|
||||
" fwknopd server can ignore such requests).\n"
|
||||
" -S, --source-port - Set the source port for outgoing SPA packet.\n"
|
||||
" -Q, --spoof-source - Set the source IP for outgoing SPA packet.\n"
|
||||
" -R, --resolve-ip-http - Resolve the external network IP by\n"
|
||||
" connecting to the URL:\n"
|
||||
" http://"
|
||||
HTTP_RESOLVE_HOST
|
||||
HTTP_RESOLVE_URL
|
||||
"\n"
|
||||
" -u, --user-agent - Set the HTTP User-Agent for resolving the\n"
|
||||
" external IP via -R, or for sending SPA\n"
|
||||
" packets over HTTP.\n"
|
||||
" -U, --spoof-user - Set the username within outgoing SPA packet.\n"
|
||||
" -q, --quiet - Perform fwknop functions quietly.\n"
|
||||
" -G, --get-key - Load an encryption key/password from a file.\n"
|
||||
" -r, --rand-port - Send the SPA packet over a randomly assigned\n"
|
||||
" port (requires a broader pcap filter on the\n"
|
||||
" server side than the default of udp 62201).\n"
|
||||
" -T, --test - Build the SPA packet but do not send it over\n"
|
||||
" the network.\n"
|
||||
" -v, --verbose - Set verbose mode.\n"
|
||||
" -V, --version - Print version number.\n"
|
||||
" -m, --digest-type - Speciy the message digest algorithm to use.\n"
|
||||
" (md5, sha1, or sha256 (default)).\n"
|
||||
" -f, --fw-timeout - Specify SPA server firewall timeout from the\n"
|
||||
" client side.\n"
|
||||
" --gpg-encryption - Use GPG encyrption (default is Rijndael).\n"
|
||||
" --gpg-recipient-key - Specify the recipient GPG key name or ID.\n"
|
||||
" --gpg-signer-key - Specify the signer's GPG key name or ID.\n"
|
||||
" --gpg-home-dir - Specify the GPG home directory.\n"
|
||||
" --gpg-agent - Use GPG agent if available.\n"
|
||||
" --nat-local - Access a local service via a forwarded port\n"
|
||||
" on the fwknopd server system.\n"
|
||||
" --nat-port - Specify the port to forward to access a\n"
|
||||
" service via NAT.\n"
|
||||
" --nat-rand-port - Have the fwknop client assign a random port\n"
|
||||
" for NAT access.\n"
|
||||
" --show-last - Show the last fwknop command line arguments.\n"
|
||||
" --time-offset-plus - Add time to outgoing SPA packet timestamp.\n"
|
||||
" --time-offset-minus - Subtract time from outgoing SPA packet\n"
|
||||
" timestamp.\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
115
client/config_init.h
Normal file
115
client/config_init.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
*
|
||||
* File: fwknop.h
|
||||
*
|
||||
* Author: Damien Stuart
|
||||
*
|
||||
* Purpose: Header file for fwknop config_init.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef CONFIG_INIT_H
|
||||
#define CONFIG_INIT_H
|
||||
|
||||
#include <getopt.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* Long options values (for those without a short option).
|
||||
*/
|
||||
enum {
|
||||
FKO_DIGEST_NAME = 0x100,
|
||||
NAT_LOCAL,
|
||||
NAT_PORT,
|
||||
NAT_RAND_PORT,
|
||||
TIME_OFFSET_MINUS,
|
||||
TIME_OFFSET_PLUS,
|
||||
NO_SAVE_ARGS,
|
||||
SHOW_LAST_ARGS,
|
||||
/* Put GPG-related items below the following line */
|
||||
GPG_ENCRYPTION = 0x200,
|
||||
GPG_RECIP_KEY,
|
||||
GPG_SIGNER_KEY,
|
||||
GPG_HOME_DIR,
|
||||
GPG_AGENT,
|
||||
NOOP /* Just to be a marker for the end */
|
||||
};
|
||||
|
||||
/* Our program command-line options...
|
||||
*/
|
||||
static struct option cmd_opts[] =
|
||||
{
|
||||
{"allow-ip", 1, NULL, 'a'},
|
||||
{"access", 1, NULL, 'A'},
|
||||
{"save-packet-append", 0, NULL, 'b'},
|
||||
{"save-packet", 1, NULL, 'B'},
|
||||
{"no-save", 0, NULL, NO_SAVE_ARGS},
|
||||
{"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 },
|
||||
{"gpg-home-dir", 1, NULL, GPG_HOME_DIR },
|
||||
{"gpg-agent", 0, NULL, GPG_AGENT },
|
||||
{"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'},
|
||||
{"spoof-src", 1, NULL, 'Q'},
|
||||
{"rand-port", 0, NULL, 'r'},
|
||||
{"resolve-ip-http", 0, NULL, 'R'},
|
||||
{"show-last", 0, NULL, SHOW_LAST_ARGS},
|
||||
{"source-ip", 0, NULL, 's'},
|
||||
{"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},
|
||||
{"user-agent", 1, NULL, 'u'},
|
||||
{"spoof-user", 1, NULL, 'U'},
|
||||
{"verbose", 0, NULL, 'v'},
|
||||
{"version", 0, NULL, 'V'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* Track config options set via command-line.
|
||||
* --DSS: XXX: These will need to be reviewed...
|
||||
*/
|
||||
typedef struct opts_track {
|
||||
unsigned int got_destination:1;
|
||||
unsigned int got_server_port:1;
|
||||
unsigned int got_server_proto:1;
|
||||
unsigned int got_config_file:1;
|
||||
unsigned int got_source_port:1;
|
||||
unsigned int got_spoof_src:1;
|
||||
} opts_track_t;
|
||||
|
||||
/* Function Prototypes
|
||||
*/
|
||||
void config_init(fko_cli_options_t *options, int argc, char **argv);
|
||||
void usage(void);
|
||||
|
||||
#endif /* CONFIG_INIT_H */
|
||||
|
||||
/***EOF***/
|
||||
721
client/fwknop.c
Normal file
721
client/fwknop.c
Normal file
@@ -0,0 +1,721 @@
|
||||
/* $Id$
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: fwknop.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: An implementation of an fwknop client.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include "fwknop.h"
|
||||
#include "config_init.h"
|
||||
#include "spa_comm.h"
|
||||
#include "utils.h"
|
||||
#include "getpasswd.h"
|
||||
|
||||
/* prototypes
|
||||
*/
|
||||
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);
|
||||
static void show_last_command(void);
|
||||
static void save_args(int argc, char **argv);
|
||||
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 resolve_ip_http(fko_cli_options_t *options);
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
fko_ctx_t ctx, ctx2;
|
||||
int res;
|
||||
char *spa_data, *version;
|
||||
char access_buf[MAX_LINE_LEN];
|
||||
|
||||
fko_cli_options_t options;
|
||||
|
||||
/* Handle command line
|
||||
*/
|
||||
config_init(&options, argc, argv);
|
||||
|
||||
/* Handle options that don't require a libfko context
|
||||
*/
|
||||
if(options.show_last_command)
|
||||
show_last_command();
|
||||
else if (!options.no_save_args)
|
||||
save_args(argc, argv);
|
||||
|
||||
/* Intialize the context
|
||||
*/
|
||||
res = fko_new(&ctx);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_new", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Display version info and exit.
|
||||
*/
|
||||
if (options.version) {
|
||||
fko_get_version(ctx, &version);
|
||||
|
||||
fprintf(stdout, "[+] fwknop client %s, FKO protocol version %s\n",
|
||||
MY_VERSION, version);
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* 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(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
/* Resolve the client's public facing IP address if requestesd.
|
||||
* if this fails, consider it fatal.
|
||||
*/
|
||||
if (options.resolve_ip_http)
|
||||
if(resolve_ip_http(&options) < 0)
|
||||
return(EXIT_FAILURE);
|
||||
|
||||
/* 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(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* 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(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up for using GPG if specified.
|
||||
*/
|
||||
if(options.use_gpg)
|
||||
{
|
||||
/* If use-gpg-agent was not specified, then remove the GPG_AGENT_INFO
|
||||
* ENV variable if it exists.
|
||||
*/
|
||||
#ifndef WIN32
|
||||
if(!options.use_gpg_agent)
|
||||
unsetenv("GPG_AGENT_INFO");
|
||||
#endif
|
||||
|
||||
res = fko_set_spa_encryption_type(ctx, FKO_ENCRYPTION_GPG);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_spa_encryption_type", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If a GPG home dir was specified, set it here. Note: Setting
|
||||
* this has to occur before calling any of the other GPG-related
|
||||
* functions.
|
||||
*/
|
||||
if(options.gpg_home_dir != NULL && strlen(options.gpg_home_dir) > 0)
|
||||
{
|
||||
res = fko_set_gpg_home_dir(ctx, options.gpg_home_dir);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_gpg_home_dir", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
res = fko_set_gpg_recipient(ctx, options.gpg_recipient_key);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_gpg_recipient", res);
|
||||
|
||||
if(IS_GPG_ERROR(res))
|
||||
fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errorstr(ctx));
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(options.gpg_signer_key != NULL && strlen(options.gpg_signer_key))
|
||||
{
|
||||
res = fko_set_gpg_signer(ctx, options.gpg_signer_key);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_gpg_signer", res);
|
||||
|
||||
if(IS_GPG_ERROR(res))
|
||||
fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errorstr(ctx));
|
||||
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set Digest type.
|
||||
*/
|
||||
if(options.digest_type)
|
||||
{
|
||||
fko_set_spa_digest_type(ctx, options.digest_type);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_spa_digest_type", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the context data (encrypt and encode the SPA data)
|
||||
*/
|
||||
res = fko_spa_data_final(ctx, get_user_pw(&options, CRYPT_OP_ENCRYPT));
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_spa_data_final", res);
|
||||
|
||||
if(IS_GPG_ERROR(res))
|
||||
fprintf(stderr, "GPG ERR: %s\n", fko_gpg_errorstr(ctx));
|
||||
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Display the context data.
|
||||
*/
|
||||
if (options.verbose || options.test)
|
||||
display_ctx(ctx);
|
||||
|
||||
/* Save packet data payload if requested.
|
||||
*/
|
||||
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
|
||||
* portion should be moved elsewhere).
|
||||
*/
|
||||
if (!options.test)
|
||||
{
|
||||
res = send_spa_packet(ctx, &options);
|
||||
if(res < 0)
|
||||
{
|
||||
fprintf(stderr, "[*] send_spa_packet: packet not sent.\n");
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(options.verbose)
|
||||
fprintf(stderr, "[+] send_spa_packet: bytes sent: %i\n", res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/************** Decoding now *****************/
|
||||
|
||||
/* Now we create a new context based on data from the first one.
|
||||
*/
|
||||
res = fko_get_spa_data(ctx, &spa_data);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_get_spa_data", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If gpg-home-dir is specified, we have to defer decrypting if we
|
||||
* use the fko_new_with_data() function because we need to set the
|
||||
* gpg home dir after the context is created, but before we attempt
|
||||
* to decrypt the data. Therefore we either pass NULL for the
|
||||
* decryption key to fko_new_with_data() or use fko_new() to create
|
||||
* an empty context, populate it with the encrypted data, set our
|
||||
* options, then decode it.
|
||||
*/
|
||||
res = fko_new_with_data(&ctx2, spa_data, NULL);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_new_with_data", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* See if we are using gpg and if we need to set the GPG home dir.
|
||||
*/
|
||||
if(options.use_gpg)
|
||||
{
|
||||
if(options.gpg_home_dir != NULL && strlen(options.gpg_home_dir) > 0)
|
||||
{
|
||||
res = fko_set_gpg_home_dir(ctx2, options.gpg_home_dir);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_set_gpg_home_dir", res);
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = fko_decrypt_spa_data(
|
||||
ctx2, get_user_pw(&options, CRYPT_OP_DECRYPT)
|
||||
);
|
||||
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("fko_decrypt_spa_data", res);
|
||||
|
||||
if(IS_GPG_ERROR(res)) {
|
||||
/* we most likely could not decrypt the gpg-encrypted data
|
||||
* because we don't have access to the private key associated
|
||||
* with the public key we used for encryption. Since this is
|
||||
* expected, return 0 instead of an error condition (so calling
|
||||
* programs like the fwknop test suite don't interpret this as
|
||||
* an unrecoverable error), but print the error string for
|
||||
debugging purposes. */
|
||||
fprintf(stderr, "GPG ERR: %s\n%s\n", fko_gpg_errorstr(ctx2),
|
||||
"[*] No access to recipient private key?\n");
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("\nDump of the Decoded Data\n");
|
||||
display_ctx(ctx2);
|
||||
|
||||
fko_destroy(ctx2);
|
||||
}
|
||||
|
||||
fko_destroy(ctx);
|
||||
|
||||
return(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
port = (MIN_HIGH_PORT + (abs(atoi(rand_val)) % (MAX_PORT - MIN_HIGH_PORT)));
|
||||
|
||||
/* Force libfko to calculate a new random value since we don't want to
|
||||
* given anyone a hint (via the port value) about the contents of the
|
||||
* encrypted SPA data.
|
||||
*/
|
||||
res = fko_set_rand_value(ctx, NULL);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
errmsg("get_rand_port(), fko_get_rand_value", res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return 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;
|
||||
int st_len = strlen(str);
|
||||
|
||||
for (i=0; i < st_len; 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);
|
||||
}
|
||||
|
||||
static int
|
||||
get_save_file(char *args_save_file)
|
||||
{
|
||||
char *homedir = NULL;
|
||||
int rv = 0;
|
||||
|
||||
homedir = getenv("HOME");
|
||||
|
||||
if (homedir != NULL) {
|
||||
snprintf(args_save_file, MAX_PATH_LEN, "%s%s%s",
|
||||
homedir, "/", ".fwknop.run");
|
||||
rv = 1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Show the last command that was executed
|
||||
*/
|
||||
static void
|
||||
show_last_command(void)
|
||||
{
|
||||
char args_save_file[MAX_PATH_LEN];
|
||||
char args_str[MAX_LINE_LEN] = "";
|
||||
FILE *args_file_ptr = NULL;
|
||||
|
||||
#ifdef WIN32
|
||||
/* Not sure what the right thing is here on Win32, just exit
|
||||
* for now.
|
||||
*/
|
||||
printf("[*] --show-last not implemented on Win32 yet.");
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
|
||||
if (get_save_file(args_save_file)) {
|
||||
if ((args_file_ptr = fopen(args_save_file, "r")) == NULL) {
|
||||
printf("[*] Could not open args file: %s\n",
|
||||
args_save_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((fgets(args_str, MAX_LINE_LEN, args_file_ptr)) != NULL) {
|
||||
printf("[+] Last fwknop client command line: %s", args_str);
|
||||
} else {
|
||||
printf("[-] Could not read line from file: %s\n", args_save_file);
|
||||
}
|
||||
fclose(args_file_ptr);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Save our command line arguments
|
||||
*/
|
||||
static void
|
||||
save_args(int argc, char **argv)
|
||||
{
|
||||
char args_save_file[MAX_PATH_LEN];
|
||||
char args_str[MAX_LINE_LEN] = "";
|
||||
FILE *args_file_ptr = NULL;
|
||||
int i = 0, args_str_len;
|
||||
|
||||
#ifdef WIN32
|
||||
/* Not sure what the right thing is here on Win32, just return
|
||||
* for now.
|
||||
*/
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
if (get_save_file(args_save_file)) {
|
||||
if ((args_file_ptr = fopen(args_save_file, "w")) == NULL) {
|
||||
printf("[*] Could not open args file: %s\n",
|
||||
args_save_file);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
for (i=0; i < argc; i++) {
|
||||
args_str_len += strlen(argv[i]);
|
||||
if (args_str_len >= MAX_PATH_LEN) {
|
||||
printf("[*] argument string too long, exiting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strlcat(args_str, argv[i], MAX_PATH_LEN);
|
||||
strlcat(args_str, " ", MAX_PATH_LEN);
|
||||
}
|
||||
fprintf(args_file_ptr, "%s\n", args_str);
|
||||
fclose(args_file_ptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
return fko_set_spa_message_type(ctx, message_type);
|
||||
}
|
||||
|
||||
/* Prompt for and receive a user password.
|
||||
*/
|
||||
char*
|
||||
get_user_pw(fko_cli_options_t *options, int crypt_op)
|
||||
{
|
||||
if (options->get_key_file[0] != 0x0) {
|
||||
/* grab the key/password from the --get-key file
|
||||
*/
|
||||
return(getpasswd_file(options->get_key_file,
|
||||
options->spa_server_str));
|
||||
}
|
||||
else if (options->use_gpg) {
|
||||
return(options->use_gpg_agent ? ""
|
||||
: getpasswd("Enter passphrase for secret key: "));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(crypt_op == CRYPT_OP_ENCRYPT)
|
||||
return(getpasswd("Enter encryption password: "));
|
||||
else if(crypt_op == CRYPT_OP_DECRYPT)
|
||||
return(getpasswd("Enter decryption password: "));
|
||||
else
|
||||
return(getpasswd("Enter password: "));
|
||||
}
|
||||
}
|
||||
|
||||
/* Display an FKO error message.
|
||||
*/
|
||||
void
|
||||
errmsg(char *msg, int err) {
|
||||
fprintf(stderr, "[*] %s: %s: Error %i - %s\n",
|
||||
MY_NAME, msg, err, fko_errstr(err));
|
||||
}
|
||||
|
||||
/* Show the fields of the FKO context.
|
||||
*/
|
||||
static void
|
||||
display_ctx(fko_ctx_t ctx)
|
||||
{
|
||||
char *rand_val = NULL;
|
||||
char *username = NULL;
|
||||
char *version = NULL;
|
||||
char *spa_message = NULL;
|
||||
char *nat_access = NULL;
|
||||
char *server_auth = NULL;
|
||||
char *enc_data = NULL;
|
||||
char *spa_digest = NULL;
|
||||
char *spa_data = NULL;
|
||||
|
||||
time_t timestamp = 0;
|
||||
short msg_type = -1;
|
||||
short digest_type = -1;
|
||||
int client_timeout = -1;
|
||||
|
||||
/* Should be checking return values, but this is temp code. --DSS
|
||||
*/
|
||||
fko_get_rand_value(ctx, &rand_val);
|
||||
fko_get_username(ctx, &username);
|
||||
fko_get_timestamp(ctx, ×tamp);
|
||||
fko_get_version(ctx, &version);
|
||||
fko_get_spa_message_type(ctx, &msg_type);
|
||||
fko_get_spa_message(ctx, &spa_message);
|
||||
fko_get_spa_nat_access(ctx, &nat_access);
|
||||
fko_get_spa_server_auth(ctx, &server_auth);
|
||||
fko_get_spa_client_timeout(ctx, &client_timeout);
|
||||
fko_get_spa_digest_type(ctx, &digest_type);
|
||||
fko_get_encoded_data(ctx, &enc_data);
|
||||
fko_get_spa_digest(ctx, &spa_digest);
|
||||
fko_get_spa_data(ctx, &spa_data);
|
||||
|
||||
printf("\nFKO Field Values:\n=================\n\n");
|
||||
printf(" Random Value: %s\n", rand_val == NULL ? "<NULL>" : rand_val);
|
||||
printf(" Username: %s\n", username == NULL ? "<NULL>" : username);
|
||||
printf(" Timestamp: %u\n", (unsigned int) timestamp);
|
||||
printf(" FKO Version: %s\n", version == NULL ? "<NULL>" : version);
|
||||
printf(" Message Type: %i\n", msg_type);
|
||||
printf(" Message String: %s\n", spa_message == NULL ? "<NULL>" : spa_message);
|
||||
printf(" Nat Access: %s\n", nat_access == NULL ? "<NULL>" : nat_access);
|
||||
printf(" Server Auth: %s\n", server_auth == NULL ? "<NULL>" : server_auth);
|
||||
printf(" Client Timeout: %u\n", client_timeout);
|
||||
printf(" Digest Type: %u\n", digest_type);
|
||||
printf("\n Encoded Data: %s\n", enc_data == NULL ? "<NULL>" : enc_data);
|
||||
printf("\nSPA Data Digest: %s\n", spa_digest == NULL ? "<NULL>" : spa_digest);
|
||||
printf("\nFinal Packed/Encrypted/Encoded Data:\n\n%s\n\n", spa_data);
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
37
client/fwknop.h
Normal file
37
client/fwknop.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* $Id$
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: fwknop.h
|
||||
*
|
||||
* Author: Damien S. Stuart (dstuart@dstuart.org)
|
||||
* Michael Rash (mbr@cipherdyne.org)
|
||||
*
|
||||
* Purpose: Header file for fwknop client test program.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#ifndef FWKNOP_H
|
||||
#define FWKNOP_H
|
||||
|
||||
#include "fwknop_common.h"
|
||||
|
||||
/* Used by the get_user_pw function below.
|
||||
*/
|
||||
#define CRYPT_OP_ENCRYPT 1
|
||||
#define CRYPT_OP_DECRYPT 2
|
||||
|
||||
#endif /* FWKNOP_H */
|
||||
122
client/fwknop_common.h
Normal file
122
client/fwknop_common.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
*
|
||||
* File: fwknop_common.h
|
||||
*
|
||||
* Author: Damien Stuart
|
||||
*
|
||||
* Purpose: Header file for fwknop config_init.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#ifndef FWKNOP_COMMON_H
|
||||
#define FWKNOP_COMMON_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* My Name and Version
|
||||
*/
|
||||
#define MY_NAME "fwknop"
|
||||
#define MY_DESC "Single Packet Authorization client"
|
||||
|
||||
/* Get our program version from VERSION (defined in config.h).
|
||||
*/
|
||||
#define MY_VERSION VERSION
|
||||
|
||||
/* Default config path, can override with -c
|
||||
*/
|
||||
#define DEF_CONFIG_FILE MY_NAME".conf"
|
||||
|
||||
/* For time offset handling
|
||||
*/
|
||||
#define MAX_TIME_STR_LEN 9
|
||||
#define TIME_OFFSET_SECONDS 1
|
||||
#define TIME_OFFSET_MINUTES 60
|
||||
#define TIME_OFFSET_HOURS 3600
|
||||
#define TIME_OFFSET_DAYS 86400
|
||||
|
||||
/* For resolving the allow IP via HTTP and sending SPA packets over
|
||||
* HTTP
|
||||
*/
|
||||
#define HTTP_RESOLVE_HOST "www.cipherdyne.org"
|
||||
#define HTTP_RESOLVE_URL "/cgi/myip.cgi"
|
||||
#define HTTP_MAX_REQUEST_LEN 2000
|
||||
#define HTTP_MAX_RESPONSE_LEN 2000
|
||||
#define HTTP_MAX_USER_AGENT_LEN 50
|
||||
|
||||
/* fwknop 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;
|
||||
int show_last_command;
|
||||
int no_save_args;
|
||||
char spa_server_str[MAX_SERVER_STR_LEN]; /* may be a hostname */
|
||||
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;
|
||||
|
||||
/* External IP resolution via HTTP
|
||||
*/
|
||||
int resolve_ip_http;
|
||||
char http_user_agent[HTTP_MAX_USER_AGENT_LEN];
|
||||
|
||||
/* SPA packet transmission port and protocol
|
||||
*/
|
||||
int spa_proto;
|
||||
unsigned int spa_dst_port;
|
||||
unsigned int spa_src_port; /* only used with --source-port */
|
||||
|
||||
unsigned int digest_type;
|
||||
|
||||
/* Various command-line flags */
|
||||
unsigned char quiet; /* --quiet mode */
|
||||
unsigned char verbose; /* --verbose mode */
|
||||
unsigned char version; /* --version */
|
||||
unsigned char no_save;
|
||||
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];
|
||||
|
||||
} fko_cli_options_t;
|
||||
|
||||
extern fko_cli_options_t options;
|
||||
|
||||
#endif /* FWKNOP_COMMON_H */
|
||||
|
||||
/***EOF***/
|
||||
213
client/getpasswd.c
Normal file
213
client/getpasswd.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/* $Id$
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: getpasswd.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: Routines for obtaining a password from a user.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#include "fwknop_common.h"
|
||||
#include "getpasswd.h"
|
||||
|
||||
#define MAX_PASS_LEN 128
|
||||
|
||||
/* Function for accepting password input from users
|
||||
*/
|
||||
char*
|
||||
getpasswd(const char *prompt)
|
||||
{
|
||||
static char pwbuf[MAX_PASS_LEN + 1] = {0};
|
||||
char *ptr;
|
||||
int c;
|
||||
|
||||
#ifndef WIN32
|
||||
FILE *fp;
|
||||
sigset_t sig, old_sig;
|
||||
struct termios ts, old_ts;
|
||||
|
||||
if((fp = fopen(ctermid(NULL), "r+")) == NULL)
|
||||
return(NULL);
|
||||
|
||||
setbuf(fp, NULL);
|
||||
|
||||
/* Setup blocks for SIGINT and SIGTSTP and save the original signal
|
||||
* mask.
|
||||
*/
|
||||
sigemptyset(&sig);
|
||||
sigaddset(&sig, SIGINT);
|
||||
sigaddset(&sig, SIGTSTP);
|
||||
sigprocmask(SIG_BLOCK, &sig, &old_sig);
|
||||
|
||||
/* Save current tty state for later restoration after we disable echo
|
||||
* of characters to the tty.
|
||||
*/
|
||||
tcgetattr(fileno(fp), &ts);
|
||||
old_ts = ts;
|
||||
ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
|
||||
tcsetattr(fileno(fp), TCSAFLUSH, &ts);
|
||||
|
||||
fputs(prompt, fp);
|
||||
#endif
|
||||
|
||||
/* Read in the password.
|
||||
*/
|
||||
ptr = pwbuf;
|
||||
#ifdef WIN32
|
||||
_cputs(prompt);
|
||||
while((c = _getch()) != '\r')
|
||||
{
|
||||
/* Handle a backspace without backing up too far.
|
||||
*/
|
||||
if(c == '\b')
|
||||
{
|
||||
if(ptr != pwbuf)
|
||||
*ptr--;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Handle a Ctrl-U to clear the password entry and start over
|
||||
* (like it works under Unix).
|
||||
*/
|
||||
if(c == 0x15)
|
||||
{
|
||||
ptr = pwbuf;
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
while((c = getc(fp)) != EOF && c != '\n')
|
||||
{
|
||||
#endif
|
||||
if(ptr < &pwbuf[MAX_PASS_LEN])
|
||||
*ptr++ = c;
|
||||
}
|
||||
|
||||
/* Null terminate the password.
|
||||
*/
|
||||
*ptr = 0;
|
||||
|
||||
#ifndef WIN32
|
||||
/* we can go ahead and echo out a newline.
|
||||
*/
|
||||
putc('\n', fp);
|
||||
|
||||
/* Restore our tty state and signal handlers.
|
||||
*/
|
||||
tcsetattr(fileno(fp), TCSAFLUSH, &old_ts);
|
||||
sigprocmask(SIG_BLOCK, &old_sig, NULL);
|
||||
|
||||
fclose(fp);
|
||||
#else
|
||||
/* In Windows, it would be a CR-LF
|
||||
*/
|
||||
_putch('\r');
|
||||
_putch('\n');
|
||||
#endif
|
||||
|
||||
return(pwbuf);
|
||||
}
|
||||
|
||||
/* Function for accepting password input from from a file
|
||||
*/
|
||||
char*
|
||||
getpasswd_file(const char *pw_file, const char *server_str)
|
||||
{
|
||||
FILE *pwfile_ptr;
|
||||
unsigned int numLines = 0, i = 0, found_dst;
|
||||
|
||||
static char pwbuf[MAX_PASS_LEN + 1] = {0};
|
||||
char conf_line_buf[MAX_LINE_LEN] = {0};
|
||||
char tmp_char_buf[MAX_LINE_LEN] = {0};
|
||||
char *lptr;
|
||||
|
||||
if ((pwfile_ptr = fopen(pw_file, "r")) == NULL)
|
||||
{
|
||||
fprintf(stderr, "[*] Could not open config file: %s\n", pw_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((fgets(conf_line_buf, MAX_LINE_LEN, pwfile_ptr)) != NULL)
|
||||
{
|
||||
numLines++;
|
||||
conf_line_buf[MAX_LINE_LEN-1] = '\0';
|
||||
lptr = conf_line_buf;
|
||||
|
||||
memset(tmp_char_buf, 0x0, MAX_LINE_LEN);
|
||||
|
||||
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
|
||||
lptr++;
|
||||
|
||||
/* Get past comments and empty lines.
|
||||
*/
|
||||
if (*lptr == '#' || *lptr == '\n' || *lptr == '\r' || *lptr == '\0' || *lptr == ';')
|
||||
continue;
|
||||
|
||||
/* Look for a line like "<SPA destination IP>: <password>" - this allows
|
||||
* multiple keys to be placed within the same file, and the client will
|
||||
* reference the matching one for the SPA server we are contacting
|
||||
*/
|
||||
found_dst = 1;
|
||||
for (i=0; i < strlen(server_str); i++)
|
||||
if (*lptr++ != server_str[i])
|
||||
found_dst = 0;
|
||||
|
||||
if (! found_dst)
|
||||
continue;
|
||||
|
||||
if (*lptr == ':')
|
||||
lptr++;
|
||||
else
|
||||
continue;
|
||||
|
||||
/* Skip whitespace until we get to the password
|
||||
*/
|
||||
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
|
||||
lptr++;
|
||||
|
||||
i = 0;
|
||||
while (*lptr != '\0' && *lptr != '\n') {
|
||||
pwbuf[i] = *lptr;
|
||||
lptr++;
|
||||
i++;
|
||||
}
|
||||
pwbuf[i] = '\0';
|
||||
}
|
||||
|
||||
fclose(pwfile_ptr);
|
||||
|
||||
if (pwbuf[0] == '\0') {
|
||||
fprintf(stderr, "[*] Could not get password for IP: %s from: %s\n",
|
||||
server_str, pw_file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return pwbuf;
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
34
client/getpasswd.h
Normal file
34
client/getpasswd.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: getpasswd.h
|
||||
*
|
||||
* Author: Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* Purpose: Header file for getpasswd.c.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#ifndef GETPASSWD_H
|
||||
#define GETPASSWD_H
|
||||
|
||||
/* Prototypes
|
||||
*/
|
||||
char* getpasswd(const char *prompt);
|
||||
char* getpasswd_file(const char *pw_file, const char *server_str);
|
||||
|
||||
#endif /* GETPASSWD_H */
|
||||
140
client/http_resolve_host.c
Normal file
140
client/http_resolve_host.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: http_resolve_host.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: Routine for using an http request to obtain a client's IP
|
||||
* address as seen from the outside world.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include "fwknop_common.h"
|
||||
#include <netdb.h>
|
||||
|
||||
int
|
||||
resolve_ip_http(fko_cli_options_t *options)
|
||||
{
|
||||
int sock, res, error, http_buf_len, i;
|
||||
struct addrinfo *result, *rp, hints;
|
||||
char http_buf[HTTP_MAX_REQUEST_LEN];
|
||||
char http_response[HTTP_MAX_RESPONSE_LEN];
|
||||
|
||||
/* Build our HTTP request to resolve the external IP (this is similar to
|
||||
* to contacting whatismyip.org, but using a different URL).
|
||||
*/
|
||||
snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
|
||||
"GET %s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: */*\r\n"
|
||||
"Host: %s\r\nConnection: Keep-Alive\r\n\r\n",
|
||||
HTTP_RESOLVE_URL, options->http_user_agent, HTTP_RESOLVE_HOST
|
||||
);
|
||||
|
||||
http_buf_len = strlen(http_buf);
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
error = getaddrinfo(HTTP_RESOLVE_HOST, "80", &hints, &result);
|
||||
if (error != 0)
|
||||
{
|
||||
fprintf(stderr, "[*] error in getaddrinfo: %s\n", gai_strerror(error));
|
||||
return(-1);
|
||||
}
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sock = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
|
||||
if (error = connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
|
||||
break; /* made it */
|
||||
|
||||
#ifdef WIN32
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rp == NULL) {
|
||||
perror("[*] resolve_ip_http: Could not create socket: ");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
res = send(sock, http_buf, http_buf_len, 0);
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
perror("[*] resolve_ip_http: write error: ");
|
||||
}
|
||||
else if(res != http_buf_len)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[#] Warning: bytes sent (%i) not spa data length (%i).\n",
|
||||
res, http_buf_len
|
||||
);
|
||||
}
|
||||
|
||||
res = recv(sock, http_response, HTTP_MAX_RESPONSE_LEN, 0);
|
||||
http_response[HTTP_MAX_RESPONSE_LEN-1] = '\0';
|
||||
|
||||
#ifdef WIN32
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
|
||||
/* Now parse the response for the IP address (which should be at
|
||||
* the end of the string
|
||||
*/
|
||||
for (i=res-3; i >= 0; i--)
|
||||
{
|
||||
if(http_response[i] == '\n')
|
||||
break;
|
||||
if(http_response[i] != '.' && ! isdigit(http_response[i]))
|
||||
{
|
||||
fprintf(stderr, "[*] Invalid IP in HTTP response.\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if (i < MIN_IP_STR_LEN)
|
||||
{
|
||||
fprintf(stderr, "[*] Invalid IP in HTTP response.\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
http_response[res-1] = '\0';
|
||||
|
||||
strlcpy(options->allow_ip_str,
|
||||
(http_response + i+1), (res - (i+2)));
|
||||
|
||||
if(options->verbose)
|
||||
printf("[+] Resolved external IP (via http://%s%s) as: %s\n",
|
||||
HTTP_RESOLVE_HOST, HTTP_RESOLVE_URL, options->allow_ip_str);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
536
client/spa_comm.c
Normal file
536
client/spa_comm.c
Normal file
@@ -0,0 +1,536 @@
|
||||
/* $Id$
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: spa_comm.c
|
||||
*
|
||||
* Author: Damien S. Stuart (dstuart@dstuart.org)
|
||||
* Michael Rash (mbr@cipherdyne.org)
|
||||
*
|
||||
* Purpose: Network-related functions for the fwknop client
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include "spa_comm.h"
|
||||
|
||||
/* Function to generate a header checksum.
|
||||
*/
|
||||
unsigned short
|
||||
chksum(unsigned short *buf, int nbytes)
|
||||
{
|
||||
unsigned int sum;
|
||||
unsigned short oddbyte;
|
||||
|
||||
sum = 0;
|
||||
while (nbytes > 1)
|
||||
{
|
||||
sum += *buf++;
|
||||
nbytes -= 2;
|
||||
}
|
||||
|
||||
if (nbytes == 1)
|
||||
{
|
||||
oddbyte = 0;
|
||||
*((unsigned short *) &oddbyte) = *(unsigned short *) buf;
|
||||
sum += oddbyte;
|
||||
}
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
|
||||
return (unsigned short) ~sum;
|
||||
}
|
||||
|
||||
static int is_ip(char *str)
|
||||
{
|
||||
int rv = 1;
|
||||
unsigned int i;
|
||||
|
||||
for (i=0; i < strlen(str); i++) {
|
||||
if (str[i] != '.' && ! isdigit(str[i])) {
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Send the SPA data via UDP packet.
|
||||
*/
|
||||
int
|
||||
send_spa_packet_tcp_or_udp(char *spa_data, int sd_len, fko_cli_options_t *options)
|
||||
{
|
||||
int sock, res, error;
|
||||
struct addrinfo *result, *rp, hints;
|
||||
char port_str[MAX_PORT_STR_LEN];
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
|
||||
if (options->spa_proto == FKO_PROTO_UDP)
|
||||
{
|
||||
/* Send the SPA data packet via an single UDP packet - this is the
|
||||
* most common usage.
|
||||
*/
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Send the SPA data packet via an established TCP connection.
|
||||
*/
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
}
|
||||
|
||||
sprintf(port_str, "%d", options->spa_dst_port);
|
||||
|
||||
error = getaddrinfo(options->spa_server_str, port_str, &hints, &result);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
fprintf(stderr, "[*] error in getaddrinfo: %s\n", gai_strerror(error));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
sock = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
|
||||
if (error = connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
|
||||
break; /* made it */
|
||||
|
||||
#ifdef WIN32
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rp == NULL) {
|
||||
perror("[*] send_spa_packet_tcp_or_udp: Could not create socket: ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
res = send(sock, spa_data, sd_len, 0);
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
perror("[*] send_spa_packet_tcp_or_udp: write error: ");
|
||||
}
|
||||
else if(res != sd_len)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[#] Warning: bytes sent (%i) not spa data length (%i).\n",
|
||||
res, sd_len
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
closesocket(sock);
|
||||
#else
|
||||
close(sock);
|
||||
#endif
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
/* Send the SPA data via raw TCP packet.
|
||||
*/
|
||||
int
|
||||
send_spa_packet_tcp_raw(char *spa_data, int sd_len, struct sockaddr_in *saddr,
|
||||
struct sockaddr_in *daddr, fko_cli_options_t *options)
|
||||
{
|
||||
#ifdef WIN32
|
||||
fprintf(stderr,
|
||||
"[*] send_spa_packet_tcp_raw: raw packets are not yet supported.\n");
|
||||
return(-1);
|
||||
#else
|
||||
int sock, res;
|
||||
char pkt_data[2048] = {0}; /* Should be enough for our purposes */
|
||||
|
||||
struct iphdr *iph = (struct iphdr *) pkt_data;
|
||||
struct tcphdr *tcph = (struct tcphdr *) (pkt_data + sizeof (struct iphdr));
|
||||
|
||||
int hdrlen = sizeof(struct iphdr) + sizeof(struct tcphdr);
|
||||
|
||||
/* Values for setsockopt.
|
||||
*/
|
||||
int one = 1;
|
||||
const int *so_val = &one;
|
||||
|
||||
sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (sock < 0)
|
||||
{
|
||||
perror("[*] send_spa_packet_tcp_raw: create socket: ");
|
||||
return(sock);
|
||||
}
|
||||
|
||||
/* Put the spa data in place.
|
||||
*/
|
||||
memcpy((pkt_data + hdrlen), spa_data, sd_len);
|
||||
|
||||
/* Construct our own header by filling in the ip/tcp header values,
|
||||
* starting with the IP header values.
|
||||
*/
|
||||
iph->ihl = 5;
|
||||
iph->version = 4;
|
||||
iph->tos = 0;
|
||||
/* Total size is header plus payload */
|
||||
iph->tot_len = hdrlen + sd_len;
|
||||
/* The value here does not matter */
|
||||
iph->id = random() & 0xffff;
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 255;
|
||||
iph->protocol = IPPROTO_TCP;
|
||||
iph->check = 0;
|
||||
iph->saddr = saddr->sin_addr.s_addr;
|
||||
iph->daddr = daddr->sin_addr.s_addr;
|
||||
|
||||
/* Now the TCP header values.
|
||||
*/
|
||||
tcph->source = saddr->sin_port;
|
||||
tcph->dest = daddr->sin_port;
|
||||
tcph->seq = htonl(1);
|
||||
tcph->ack_seq = 0;
|
||||
tcph->doff = 5;
|
||||
tcph->res1 = 0;
|
||||
/* TCP flags */
|
||||
tcph->fin = 0;
|
||||
tcph->syn = 1;
|
||||
tcph->rst = 0;
|
||||
tcph->psh = 0;
|
||||
tcph->ack = 0;
|
||||
tcph->urg = 0;
|
||||
|
||||
tcph->res2 = 0;
|
||||
tcph->window = htons(32767);
|
||||
tcph->check = 0;
|
||||
tcph->urg_ptr = 0;
|
||||
|
||||
/* No we can compute our checksum.
|
||||
*/
|
||||
iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
|
||||
|
||||
/* Make sure the kernel knows the header is included in the data so it
|
||||
* doesn't try to insert its own header into the packet.
|
||||
*/
|
||||
if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
|
||||
perror("[*] send_spa_packet_tcp_raw: setsockopt HDRINCL: ");
|
||||
|
||||
res = sendto (sock, pkt_data, iph->tot_len, 0,
|
||||
(struct sockaddr *)daddr, sizeof(*daddr));
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
perror("[*] send_spa_packet_tcp_raw: sendto error: ");
|
||||
}
|
||||
else if(res != sd_len)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[#] Warning: bytes sent (%i) not spa data length (%i).\n",
|
||||
res, sd_len
|
||||
);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
return(res);
|
||||
|
||||
#endif /* !WIN32 */
|
||||
}
|
||||
|
||||
/* Send the SPA data via ICMP packet.
|
||||
*/
|
||||
int
|
||||
send_spa_packet_icmp(char *spa_data, int sd_len, struct sockaddr_in *saddr,
|
||||
struct sockaddr_in *daddr, fko_cli_options_t *options)
|
||||
{
|
||||
#ifdef WIN32
|
||||
fprintf(stderr, "[*] send_spa_packet_icmp: raw packets are not yet supported.\n");
|
||||
return(-1);
|
||||
#else
|
||||
int res;
|
||||
char pkt_data[2048] = {0};
|
||||
|
||||
struct iphdr *iph = (struct iphdr *) pkt_data;
|
||||
struct icmphdr *icmph = (struct icmphdr *) (pkt_data + sizeof (struct iphdr));
|
||||
|
||||
int hdrlen = sizeof(struct iphdr) + sizeof(struct icmphdr);
|
||||
|
||||
/* Values for setsockopt.
|
||||
*/
|
||||
int one = 1;
|
||||
const int *so_val = &one;
|
||||
|
||||
int sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
|
||||
if (sock < 0)
|
||||
{
|
||||
perror("[*] send_spa_packet_icmp: create socket: ");
|
||||
return(sock);
|
||||
}
|
||||
|
||||
/* Put the spa data in place.
|
||||
*/
|
||||
memcpy((pkt_data + hdrlen), spa_data, sd_len);
|
||||
|
||||
/* Construct our own header by filling in the ip/icmp header values,
|
||||
* starting with the IP header values.
|
||||
*/
|
||||
iph->ihl = 5;
|
||||
iph->version = 4;
|
||||
iph->tos = 0;
|
||||
/* Total size is header plus payload */
|
||||
iph->tot_len = hdrlen + sd_len;
|
||||
/* The value here does not matter */
|
||||
iph->id = random() & 0xffff;
|
||||
iph->frag_off = 0;
|
||||
iph->ttl = 255;
|
||||
iph->protocol = IPPROTO_ICMP;
|
||||
iph->check = 0;
|
||||
iph->saddr = saddr->sin_addr.s_addr;
|
||||
iph->daddr = daddr->sin_addr.s_addr;
|
||||
|
||||
/* Now the ICMP header values.
|
||||
*/
|
||||
icmph->type = ICMP_ECHOREPLY; /* Make it an echo reply */
|
||||
icmph->code = 0;
|
||||
icmph->checksum = 0;
|
||||
|
||||
/* No we can compute our checksum.
|
||||
*/
|
||||
iph->check = chksum((unsigned short *)pkt_data, iph->tot_len);
|
||||
icmph->checksum = chksum((unsigned short *)icmph, sizeof(struct icmphdr) + sd_len);
|
||||
|
||||
/* Make sure the kernel knows the header is included in the data so it
|
||||
* doesn't try to insert its own header into the packet.
|
||||
*/
|
||||
if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, so_val, sizeof(one)) < 0)
|
||||
perror("[*] send_spa_packet_icmp: setsockopt HDRINCL: ");
|
||||
|
||||
res = sendto (sock, pkt_data, iph->tot_len, 0,
|
||||
(struct sockaddr *)daddr, sizeof(*daddr));
|
||||
|
||||
if(res < 0)
|
||||
{
|
||||
perror("[*] send_spa_packet_icmp: sendto error: ");
|
||||
}
|
||||
else if(res != sd_len)
|
||||
{
|
||||
fprintf(stderr, "[#] Warning: bytes sent (%i) not spa data length (%i).\n",
|
||||
res, sd_len);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
|
||||
return(res);
|
||||
|
||||
#endif /* !WIN32 */
|
||||
}
|
||||
|
||||
/* Send the SPA data packet via an HTTP request
|
||||
*/
|
||||
int
|
||||
send_spa_packet_http(char *spa_data, int sd_len, fko_cli_options_t *options)
|
||||
{
|
||||
char http_buf[HTTP_MAX_REQUEST_LEN];
|
||||
int i;
|
||||
|
||||
/* change "+" chars to "-", and "/" to "_" for HTTP requests (the server
|
||||
* side will translate these back before decrypting) */
|
||||
for (i=0; i < sd_len; i++) {
|
||||
if (spa_data[i] == '+') {
|
||||
spa_data[i] = '-';
|
||||
}
|
||||
else if (spa_data[i] == '/') {
|
||||
spa_data[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(http_buf, HTTP_MAX_REQUEST_LEN,
|
||||
"%s%s%s%s%s%s%s",
|
||||
"GET ",
|
||||
spa_data,
|
||||
" HTTP/1.0\r\nUser-Agent: ",
|
||||
options->http_user_agent,
|
||||
"\r\nAccept: */*\r\nHost: ",
|
||||
options->spa_server_str, /* hostname or IP */
|
||||
"\r\nConnection: Keep-Alive\r\n\r\n"
|
||||
);
|
||||
|
||||
return send_spa_packet_tcp_or_udp(http_buf, strlen(http_buf), options);
|
||||
}
|
||||
|
||||
/* Function used to send the SPA data.
|
||||
*/
|
||||
int
|
||||
send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options)
|
||||
{
|
||||
int res, sd_len;
|
||||
char *spa_data;
|
||||
|
||||
struct sockaddr_in saddr, daddr;
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsa_data;
|
||||
#endif
|
||||
|
||||
/* Get our spa data here.
|
||||
*/
|
||||
res = fko_get_spa_data(ctx, &spa_data);
|
||||
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"send_spa_packet: Error #%i from fko_get_spa_data: %s\n",
|
||||
res, fko_errstr(res)
|
||||
);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
sd_len = strlen(spa_data);
|
||||
|
||||
#ifdef WIN32
|
||||
/* Winsock needs to be initialized...
|
||||
*/
|
||||
res = WSAStartup( MAKEWORD(1,1), &wsa_data );
|
||||
if( res != 0 )
|
||||
{
|
||||
fprintf(stderr, "[*] Winsock initialization error %d\n", res );
|
||||
return(-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
errno = 0;
|
||||
|
||||
if (options->spa_proto == FKO_PROTO_TCP || options->spa_proto == FKO_PROTO_UDP)
|
||||
{
|
||||
res = send_spa_packet_tcp_or_udp(spa_data, sd_len, options);
|
||||
}
|
||||
else if (options->spa_proto == FKO_PROTO_HTTP)
|
||||
{
|
||||
res = send_spa_packet_http(spa_data, sd_len, options);
|
||||
}
|
||||
else if (options->spa_proto == FKO_PROTO_TCP_RAW
|
||||
|| options->spa_proto == FKO_PROTO_ICMP)
|
||||
{
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
memset(&daddr, 0, sizeof(daddr));
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
daddr.sin_family = AF_INET;
|
||||
|
||||
/* Set source address and port
|
||||
*/
|
||||
if (options->spa_src_port)
|
||||
saddr.sin_port = htons(options->spa_src_port);
|
||||
else
|
||||
saddr.sin_port = INADDR_ANY; /* default */
|
||||
|
||||
if (options->spoof_ip_src_str[0] != 0x00) {
|
||||
saddr.sin_addr.s_addr = inet_addr(options->spoof_ip_src_str);
|
||||
} else
|
||||
saddr.sin_addr.s_addr = INADDR_ANY; /* default */
|
||||
|
||||
if (saddr.sin_addr.s_addr == -1)
|
||||
{
|
||||
fprintf(stderr, "[*] Could not set source IP.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Set destination address and port
|
||||
*/
|
||||
daddr.sin_port = htons(options->spa_dst_port);
|
||||
daddr.sin_addr.s_addr = inet_addr(options->spa_server_str);
|
||||
|
||||
if (daddr.sin_addr.s_addr == -1)
|
||||
{
|
||||
fprintf(stderr, "[*] Could not set destination IP.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (options->spa_proto == FKO_PROTO_TCP_RAW)
|
||||
{
|
||||
res = send_spa_packet_tcp_raw(spa_data, sd_len, &saddr, &daddr, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = send_spa_packet_icmp(spa_data, sd_len, &saddr, &daddr, options);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* --DSS XXX: What to we really want to do here? */
|
||||
fprintf(stderr, "[*] %i is not a valid or supported protocol.\n",
|
||||
options->spa_proto);
|
||||
res = -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Function to write SPA packet data to the filesystem
|
||||
*/
|
||||
int write_spa_packet_data(fko_ctx_t ctx, fko_cli_options_t *options)
|
||||
{
|
||||
FILE *fp;
|
||||
char *spa_data;
|
||||
int res;
|
||||
|
||||
res = fko_get_spa_data(ctx, &spa_data);
|
||||
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"write_spa_packet_data: Error #%i from fko_get_spa_data: %s\n",
|
||||
res, fko_errstr(res)
|
||||
);
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (options->save_packet_file_append)
|
||||
{
|
||||
fp = fopen(options->save_packet_file, "a");
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink(options->save_packet_file);
|
||||
fp = fopen(options->save_packet_file, "w");
|
||||
}
|
||||
|
||||
if(fp == NULL)
|
||||
{
|
||||
perror("write_spa_packet_data: ");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
fprintf(fp, "%s\n",
|
||||
(spa_data == NULL) ? "<NULL>" : spa_data);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
147
client/spa_comm.h
Normal file
147
client/spa_comm.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: spa_comm.h
|
||||
*
|
||||
* Author: Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* Purpose: Header file for fwknop client test program.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#ifndef SPA_COMM_H
|
||||
#define SPA_COMM_H
|
||||
|
||||
#include "fwknop_common.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#if HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
/* We will roll our own packet header structs. */
|
||||
|
||||
/* The IP header
|
||||
*/
|
||||
struct iphdr
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned int ihl:4;
|
||||
unsigned int version:4;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned int version:4;
|
||||
unsigned int ihl:4;
|
||||
#else
|
||||
#error "Please fix <bits/endian.h>"
|
||||
#endif
|
||||
unsigned char tos;
|
||||
unsigned short tot_len;
|
||||
unsigned short id;
|
||||
unsigned short frag_off;
|
||||
unsigned char ttl;
|
||||
unsigned char protocol;
|
||||
unsigned short check;
|
||||
unsigned int saddr;
|
||||
unsigned int daddr;
|
||||
};
|
||||
|
||||
/* The TCP header
|
||||
*/
|
||||
struct tcphdr
|
||||
{
|
||||
unsigned short source;
|
||||
unsigned short dest;
|
||||
unsigned int seq;
|
||||
unsigned int ack_seq;
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
unsigned short res1:4;
|
||||
unsigned short doff:4;
|
||||
unsigned short fin:1;
|
||||
unsigned short syn:1;
|
||||
unsigned short rst:1;
|
||||
unsigned short psh:1;
|
||||
unsigned short ack:1;
|
||||
unsigned short urg:1;
|
||||
unsigned short res2:2;
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
unsigned short doff:4;
|
||||
unsigned short res1:4;
|
||||
unsigned short res2:2;
|
||||
unsigned short urg:1;
|
||||
unsigned short ack:1;
|
||||
unsigned short psh:1;
|
||||
unsigned short rst:1;
|
||||
unsigned short syn:1;
|
||||
unsigned short fin:1;
|
||||
#else
|
||||
#error "Adjust your <bits/endian.h> defines"
|
||||
#endif
|
||||
unsigned short window;
|
||||
unsigned short check;
|
||||
unsigned short urg_ptr;
|
||||
};
|
||||
|
||||
/* The ICMP header
|
||||
*/
|
||||
struct icmphdr
|
||||
{
|
||||
unsigned char type; /* message type */
|
||||
unsigned char code; /* type sub-code */
|
||||
unsigned short checksum;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned short id;
|
||||
unsigned short sequence;
|
||||
} echo; /* echo datagram */
|
||||
unsigned int gateway; /* gateway address */
|
||||
struct
|
||||
{
|
||||
unsigned short __unused;
|
||||
unsigned short mtu;
|
||||
} frag; /* path mtu discovery */
|
||||
} un;
|
||||
};
|
||||
|
||||
#define ICMP_ECHOREPLY 0 /* Echo Reply */
|
||||
#define ICMP_DEST_UNREACH 3 /* Destination Unreachable */
|
||||
#define ICMP_SOURCE_QUENCH 4 /* Source Quench */
|
||||
#define ICMP_REDIRECT 5 /* Redirect (change route) */
|
||||
#define ICMP_ECHO 8 /* Echo Request */
|
||||
#define ICMP_TIME_EXCEEDED 11 /* Time Exceeded */
|
||||
#define ICMP_PARAMETERPROB 12 /* Parameter Problem */
|
||||
#define ICMP_TIMESTAMP 13 /* Timestamp Request */
|
||||
#define ICMP_TIMESTAMPREPLY 14 /* Timestamp Reply */
|
||||
#define ICMP_INFO_REQUEST 15 /* Information Request */
|
||||
#define ICMP_INFO_REPLY 16 /* Information Reply */
|
||||
#define ICMP_ADDRESS 17 /* Address Mask Request */
|
||||
#define ICMP_ADDRESSREPLY 18 /* Address Mask Reply */
|
||||
|
||||
|
||||
/* Function Prototypes
|
||||
*/
|
||||
int send_spa_packet(fko_ctx_t ctx, fko_cli_options_t *options);
|
||||
int write_spa_packet_data(fko_ctx_t ctx, fko_cli_options_t *options);
|
||||
|
||||
#endif /* SPA_COMM_H */
|
||||
68
client/utils.c
Normal file
68
client/utils.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/* $Id$
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: utils.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: General/Generic functions for the fwknop client.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "utils.h"
|
||||
|
||||
/* Generic hex dump function.
|
||||
*/
|
||||
void
|
||||
hex_dump(unsigned char *data, int size)
|
||||
{
|
||||
int ln, i, j = 0;
|
||||
char ascii_str[17] = {0};
|
||||
|
||||
for(i=0; i<size; i++)
|
||||
{
|
||||
if((i % 16) == 0)
|
||||
{
|
||||
printf(" %s\n 0x%.4x: ", ascii_str, i);
|
||||
memset(ascii_str, 0x0, 17);
|
||||
j = 0;
|
||||
}
|
||||
|
||||
printf("%.2x ", data[i]);
|
||||
|
||||
ascii_str[j++] = (data[i] < 0x20 || data[i] > 0x7e) ? '.' : data[i];
|
||||
|
||||
if(j == 8)
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
/* Remainder...
|
||||
*/
|
||||
ln = strlen(ascii_str);
|
||||
if(ln > 0)
|
||||
{
|
||||
for(i=0; i < 16-ln; i++)
|
||||
printf(" ");
|
||||
|
||||
printf(" %s\n\n", ascii_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***EOF***/
|
||||
42
client/utils.h
Normal file
42
client/utils.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: utils.h
|
||||
*
|
||||
* Author: Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* Purpose: Header file for utils.c client test program.
|
||||
*
|
||||
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU Public License):
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
|
||||
/* Prototypes
|
||||
*/
|
||||
void hex_dump(unsigned char *data, int size);
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
/* Function prototypes we need for Windows
|
||||
*/
|
||||
size_t strlcat(char *dst, const char *src, size_t siz);
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
|
||||
#endif /* UTILS_H */
|
||||
Reference in New Issue
Block a user