From 3095f0ee436540776f185ce7b6a3b7f6e059af45 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Wed, 27 Jun 2012 23:06:17 -0400 Subject: [PATCH] Added key generation support with --key-gen Added --key-gen to allow KEY_BASE64 and HMAC_KEY_BASE64 keys to be created from reading random data from /dev/random. These keys can be placed within server access.conf files and corresponding client .fwknoprc files for SPA communications. The HMAC key is not used yet with this commit, but that is coming. --- client/cmd_opts.h | 6 +- client/config_init.c | 115 ++++++++++++---- client/fwknop.c | 24 +++- client/fwknop_common.h | 15 ++- client/getpasswd.c | 8 +- client/utils.c | 23 +++- client/utils.h | 1 + lib/base64.c | 2 +- lib/cipher_funcs.c | 2 +- lib/cipher_funcs.h | 1 + lib/fko.h | 3 + lib/fko_funcs.c | 35 +++++ server/access.c | 50 +++++++ server/fwknopd_common.h | 2 + server/incoming_spa.c | 17 +++ server/utils.c | 2 +- server/utils.h | 2 +- test/conf/base64_key_access.conf | 3 + test/conf/fwknoprc_with_default_base64_key | 71 ++++++++++ test/conf/fwknoprc_with_default_key | 71 ++++++++++ test/conf/fwknoprc_with_named_key | 73 ++++++++++ test/test-fwknop.pl | 149 +++++++++++++++++++++ 22 files changed, 631 insertions(+), 44 deletions(-) create mode 100644 test/conf/base64_key_access.conf create mode 100644 test/conf/fwknoprc_with_default_base64_key create mode 100644 test/conf/fwknoprc_with_default_key create mode 100644 test/conf/fwknoprc_with_named_key diff --git a/client/cmd_opts.h b/client/cmd_opts.h index ab2f8f4a..dbfd57cb 100644 --- a/client/cmd_opts.h +++ b/client/cmd_opts.h @@ -43,6 +43,7 @@ enum { TIME_OFFSET_PLUS, NO_SAVE_ARGS, SHOW_LAST_ARGS, + RC_FILE_PATH, RESOLVE_URL, /* Put GPG-related items below the following line */ GPG_ENCRYPTION = 0x200, @@ -56,7 +57,7 @@ enum { /* Our getopt_long options string. */ -#define GETOPTS_OPTION_STRING "a:A:bB:C:D:f:gG:hH:lm:M:n:N:p:P:Q:rRsS:Tu:U:vV" +#define GETOPTS_OPTION_STRING "a:A:bB:C:D:f:gG:hH:kK:lm:M:n:N:p:P:Q:rRsS:Tu:U:vV" /* Our program command-line options... */ @@ -80,6 +81,8 @@ static struct option cmd_opts[] = {"get-key", 1, NULL, 'G'}, {"help", 0, NULL, 'h'}, {"http-proxy", 1, NULL, 'H'}, + {"key-gen", 0, NULL, 'k'}, + {"key-gen-file", 1, NULL, 'K'}, {"last-cmd", 0, NULL, 'l'}, {"nat-access", 1, NULL, 'N'}, {"named-config", 1, NULL, 'n'}, @@ -89,6 +92,7 @@ static struct option cmd_opts[] = {"server-port", 1, NULL, 'p'}, {"server-proto", 1, NULL, 'P'}, {"spoof-src", 1, NULL, 'Q'}, + {"rc-file", 1, NULL, RC_FILE_PATH}, {"rand-port", 0, NULL, 'r'}, {"resolve-ip-http", 0, NULL, 'R'}, {"resolve-url", 1, NULL, RESOLVE_URL}, diff --git a/client/config_init.c b/client/config_init.c index 2632abd8..744177ad 100644 --- a/client/config_init.c +++ b/client/config_init.c @@ -367,6 +367,39 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val) if(val[0] == 'y' || val[0] == 'Y') options->rand_port = 1; } + /* Rijndael key */ + else if(CONF_VAR_IS(var, "KEY")) + { + strlcpy(options->key, val, MAX_KEY_LEN); + options->have_key = 1; + } + /* Rijndael key (base-64 encoded) */ + else if(CONF_VAR_IS(var, "KEY_BASE64")) + { + if (! is_base64((unsigned char *) val, strlen(val))) + { + fprintf(stderr, + "KEY_BASE64 argument '%s' doesn't look like base64-encoded data.\n", + val); + exit(EXIT_FAILURE); + } + strlcpy(options->key_base64, val, MAX_KEY_LEN); + options->have_base64_key = 1; + } + /* HMAC key */ + else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64")) + { + if (! is_base64((unsigned char *) val, strlen(val))) + { + fprintf(stderr, + "HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.\n", + val); + exit(EXIT_FAILURE); + } + strlcpy(options->hmac_key_base64, val, MAX_KEY_LEN); + options->have_hmac_base64_key = 1; + } + /* Key file */ else if(CONF_VAR_IS(var, "KEY_FILE")) { @@ -437,38 +470,45 @@ process_rc(fko_cli_options_t *options) char *ndx, *emark, *homedir; -#ifdef WIN32 - homedir = getenv("USERPROFILE"); -#else - homedir = getenv("HOME"); -#endif - - if(homedir == NULL) - { - fprintf(stderr, "Warning: Unable to determine HOME directory.\n" - " No .fwknoprc file processed.\n"); - return; - } - memset(rcfile, 0x0, MAX_PATH_LEN); - strlcpy(rcfile, homedir, MAX_PATH_LEN); - - rcf_offset = strlen(rcfile); - - /* Sanity check the path to .fwknoprc. - * The preceeding path plus the path separator and '.fwknoprc' = 11 - * cannot exceed MAX_PATH_LEN. - */ - if(rcf_offset > (MAX_PATH_LEN - 11)) + if(options->rc_file[0] == 0x0) { - fprintf(stderr, "Warning: Path to .fwknoprc file is too long.\n" - " No .fwknoprc file processed.\n"); - return; - } +#ifdef WIN32 + homedir = getenv("USERPROFILE"); +#else + homedir = getenv("HOME"); +#endif - rcfile[rcf_offset] = PATH_SEP; - strlcat(rcfile, ".fwknoprc", MAX_PATH_LEN); + if(homedir == NULL) + { + fprintf(stderr, "Warning: Unable to determine HOME directory.\n" + " No .fwknoprc file processed.\n"); + return; + } + + strlcpy(rcfile, homedir, MAX_PATH_LEN); + + rcf_offset = strlen(rcfile); + + /* Sanity check the path to .fwknoprc. + * The preceeding path plus the path separator and '.fwknoprc' = 11 + * cannot exceed MAX_PATH_LEN. + */ + if(rcf_offset > (MAX_PATH_LEN - 11)) + { + fprintf(stderr, "Warning: Path to .fwknoprc file is too long.\n" + " No .fwknoprc file processed.\n"); + return; + } + + rcfile[rcf_offset] = PATH_SEP; + strlcat(rcfile, ".fwknoprc", MAX_PATH_LEN); + } + else + { + strlcpy(rcfile, options->rc_file, MAX_PATH_LEN); + } /* Open the rc file for reading, if it does not exist, then create * an initial .fwknoprc file with defaults and go on. @@ -545,6 +585,13 @@ process_rc(fko_cli_options_t *options) if((ndx = strrchr(var, ':')) != NULL) *ndx = '\0'; + /* Even though sscanf should automatically add a terminating + * NULL byte, an assumption is made that the input arrays are + * big enough, so we'll force a terminating NULL byte regardless + */ + var[MAX_LINE_LEN-1] = 0x0; + val[MAX_LINE_LEN-1] = 0x0; + if(options->verbose > 3) fprintf(stderr, "RC FILE: %s, LINE: %s\tVar: %s, Val: '%s'\n", @@ -590,6 +637,7 @@ validate_options(fko_cli_options_t *options) * the version, and must use one of [-s|-R|-a]. */ if(!options->test + && !options->key_gen && !options->version && !options->show_last_command && !options->run_last_command) @@ -677,7 +725,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv) */ set_defaults(options); - /* First pass over cmd_line args to see if a named-stanza in the + /* First pass over cmd_line args to see if a named-stanza in the * rc file is used. */ while ((cmd_arg = getopt_long(argc, argv, @@ -690,6 +738,9 @@ config_init(fko_cli_options_t *options, int argc, char **argv) options->no_save_args = 1; strlcpy(options->use_rc_stanza, optarg, MAX_LINE_LEN); break; + case RC_FILE_PATH: + strlcpy(options->rc_file, optarg, MAX_PATH_LEN); + break; case 'v': options->verbose++; break; @@ -747,6 +798,9 @@ config_init(fko_cli_options_t *options, int argc, char **argv) options->spa_proto = FKO_PROTO_HTTP; strlcpy(options->http_proxy, optarg, MAX_PATH_LEN); break; + case 'k': + options->key_gen = 1; + break; case 'l': options->run_last_command = 1; break; @@ -798,6 +852,9 @@ config_init(fko_cli_options_t *options, int argc, char **argv) case 'Q': strlcpy(options->spoof_ip_src_str, optarg, MAX_IPV4_STR_LEN); break; + case RC_FILE_PATH: + strlcpy(options->rc_file, optarg, MAX_PATH_LEN); + break; case 'r': options->rand_port = 1; break; diff --git a/client/fwknop.c b/client/fwknop.c index 52734486..fe6d2ef0 100644 --- a/client/fwknop.c +++ b/client/fwknop.c @@ -36,7 +36,7 @@ /* prototypes */ -static char * get_user_pw(fko_cli_options_t *options, const int crypt_op); +static char *get_user_pw(fko_cli_options_t *options, const int crypt_op); static void display_ctx(fko_ctx_t ctx); static void errmsg(const char *msg, const int err); static void show_last_command(void); @@ -79,9 +79,20 @@ main(int argc, char **argv) return(EXIT_FAILURE); } + /* Generate Rijndael + HMAC keys from /dev/random (base64 + * encoded) and exit. + */ + if(options.key_gen) + { + fko_key_gen(options.key_base64, options.hmac_key_base64); + printf("KEY_BASE64: %s\nHMAC_KEY_BASE64: %s\n", options.key_base64, options.hmac_key_base64); + return(EXIT_SUCCESS); + } + /* Display version info and exit. */ - if (options.version) { + if(options.version) + { fko_get_version(ctx, &version); fprintf(stdout, "fwknop client %s, FKO protocol version %s\n", @@ -738,6 +749,15 @@ get_user_pw(fko_cli_options_t *options, const int crypt_op) { pw_ptr = getpasswd_file(options->get_key_file, options->spa_server_str); } + else if (options->have_key) + { + pw_ptr = options->key; + } + else if (options->have_base64_key) + { + fko_base64_decode(options->key_base64, (unsigned char *) options->key); + pw_ptr = options->key; + } else if (options->use_gpg) { if(crypt_op == CRYPT_OP_DECRYPT) diff --git a/client/fwknop_common.h b/client/fwknop_common.h index 4e494aa7..83a46c16 100644 --- a/client/fwknop_common.h +++ b/client/fwknop_common.h @@ -55,7 +55,7 @@ #define TIME_OFFSET_DAYS 86400 /* For resolving the allow IP via HTTP and sending SPA packets over - * HTTP - http://www.whatismyip.com/automation/n09230945.asp + * HTTP - http://www.whatismyip.com/automation/n09230945.asp #define HTTP_RESOLVE_HOST "www.whatismyip.com" #define HTTP_RESOLVE_URL "/automation/n09230945.asp" * --DSS Note: The whatismyip.com site has some usage restrictions. @@ -70,6 +70,7 @@ #define MAX_HOSTNAME_LEN 70 #define MAX_URL_HOST_LEN 256 #define MAX_URL_PATH_LEN 1024 +#define MAX_KEY_LEN 128 /* fwknop client configuration parameters and values */ @@ -77,6 +78,8 @@ typedef struct fko_cli_options { char config_file[MAX_PATH_LEN]; char access_str[MAX_PATH_LEN]; + char rc_file[MAX_PATH_LEN]; + char key_gen_file[MAX_PATH_LEN]; char server_command[MAX_LINE_LEN]; char get_key_file[MAX_PATH_LEN]; char save_packet_file[MAX_PATH_LEN]; @@ -93,6 +96,15 @@ typedef struct fko_cli_options char gpg_signer_key[MAX_GPG_KEY_ID]; char gpg_home_dir[MAX_PATH_LEN]; + /* Encryption keys read from a .fwknoprc stanza + */ + char key[MAX_KEY_LEN+1]; + char key_base64[MAX_KEY_LEN+1]; + char hmac_key_base64[MAX_KEY_LEN+1]; + int have_key; + int have_base64_key; + int have_hmac_base64_key; + /* NAT access */ char nat_access_str[MAX_PATH_LEN]; @@ -128,6 +140,7 @@ typedef struct fko_cli_options int time_offset_plus; int time_offset_minus; int fw_timeout; + int key_gen; char use_rc_stanza[MAX_LINE_LEN]; unsigned char got_named_stanza; diff --git a/client/getpasswd.c b/client/getpasswd.c index e1cec86d..2d51bf6d 100644 --- a/client/getpasswd.c +++ b/client/getpasswd.c @@ -40,14 +40,12 @@ #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}; + static char pwbuf[MAX_KEY_LEN + 1] = {0}; char *ptr; int c; @@ -109,7 +107,7 @@ getpasswd(const char *prompt) while((c = getc(fp)) != EOF && c != '\n') { #endif - if(ptr < &pwbuf[MAX_PASS_LEN]) + if(ptr < &pwbuf[MAX_KEY_LEN]) *ptr++ = c; } @@ -146,7 +144,7 @@ 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}; + static char pwbuf[MAX_KEY_LEN + 1] = {0}; char conf_line_buf[MAX_LINE_LEN] = {0}; char tmp_char_buf[MAX_LINE_LEN] = {0}; char *lptr; diff --git a/client/utils.c b/client/utils.c index 5a60528b..61fa9a8e 100644 --- a/client/utils.c +++ b/client/utils.c @@ -28,8 +28,7 @@ * ***************************************************************************** */ -#include -#include +#include "fwknop_common.h" #include "utils.h" /* Generic hex dump function. @@ -69,5 +68,25 @@ hex_dump(const unsigned char *data, const int size) } } +/* Determine if a buffer contains only characters from the base64 + * encoding set +*/ +int +is_base64(const unsigned char *buf, const unsigned short int len) +{ + unsigned short int i; + int rv = 1; + + for(i=0; iverbose > 3) @@ -888,6 +902,42 @@ parse_access_file(fko_srv_options_t *opts) } add_acc_string(&(curr_acc->key), val); } + else if(CONF_VAR_IS(var, "KEY_BASE64")) + { + if(strcasecmp(val, "__CHANGEME__") == 0) + { + fprintf(stderr, + "[*] KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'\n", + curr_acc->source, opts->config[CONF_ACCESS_FILE]); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + if (! is_base64((unsigned char *) val, strlen(val))) + { + fprintf(stderr, + "KEY_BASE64 argument '%s' doesn't look like base64-encoded data.\n", + val); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + add_acc_string(&(curr_acc->key_base64), val); + } + else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64")) + { + if(strcasecmp(val, "__CHANGEME__") == 0) + { + fprintf(stderr, + "[*] HMAC_KEY_BASE64 value is not properly set in stanza source '%s' in access file: '%s'\n", + curr_acc->source, opts->config[CONF_ACCESS_FILE]); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + if (! is_base64((unsigned char *) val, strlen(val))) + { + fprintf(stderr, + "HMAC_KEY_BASE64 argument '%s' doesn't look like base64-encoded data.\n", + val); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + add_acc_string(&(curr_acc->hmac_key_base64), val); + } else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT")) { add_acc_int(&(curr_acc->fw_access_timeout), val); diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 36612276..f82d6a64 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -267,6 +267,8 @@ typedef struct acc_stanza char *restrict_ports; acc_port_list_t *rport_list; char *key; + char *key_base64; + char *hmac_key_base64; int fw_access_timeout; unsigned char enable_cmd_exec; char *cmd_exec_user; diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 23832191..7cf1ab14 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -242,8 +242,25 @@ incoming_spa(fko_srv_options_t *opts) if(enc_type == FKO_ENCRYPTION_RIJNDAEL) { if(acc->key != NULL) + { res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key, acc->encryption_mode); + } + else if (acc->key_base64 != NULL) + { + if ((acc->key = strdup(acc->key_base64)) == NULL) + { + log_msg(LOG_ERR, + "Fatal memory allocation error copying key_base64 -> key: %s", + acc->key_base64 + ); + exit(EXIT_FAILURE); + } + memset(acc->key, 0x0, strlen(acc->key_base64)); + fko_base64_decode(acc->key_base64, (unsigned char *) acc->key); + res = fko_new_with_data(&ctx, + (char *)spa_pkt->packet_data, acc->key, acc->encryption_mode); + } else { log_msg(LOG_ERR, diff --git a/server/utils.c b/server/utils.c index e145ba89..1633b9fc 100644 --- a/server/utils.c +++ b/server/utils.c @@ -164,7 +164,7 @@ is_valid_dir(const char *path) * encoding set */ int -is_base64(const unsigned char *buf, unsigned short int len) +is_base64(const unsigned char *buf, const unsigned short int len) { unsigned short int i; int rv = 1; diff --git a/server/utils.h b/server/utils.h index 09584e1f..42c5966c 100644 --- a/server/utils.h +++ b/server/utils.h @@ -60,7 +60,7 @@ */ void hex_dump(const unsigned char *data, const int size); char* dump_ctx(fko_ctx_t ctx); -int is_base64(const unsigned char *buf, unsigned short int len); +int is_base64(const unsigned char *buf, const unsigned short int len); int is_valid_dir(const char *path); size_t strlcat(char *dst, const char *src, size_t siz); diff --git a/test/conf/base64_key_access.conf b/test/conf/base64_key_access.conf new file mode 100644 index 00000000..4a52f440 --- /dev/null +++ b/test/conf/base64_key_access.conf @@ -0,0 +1,3 @@ +SOURCE: ANY; +KEY_BASE64: w8KMBQlKf4UCARNQmi25DPxw4xAUaRs21nf8nbRNzUo=; +FW_ACCESS_TIMEOUT: 3; diff --git a/test/conf/fwknoprc_with_default_base64_key b/test/conf/fwknoprc_with_default_base64_key new file mode 100644 index 00000000..1566caf7 --- /dev/null +++ b/test/conf/fwknoprc_with_default_base64_key @@ -0,0 +1,71 @@ +# .fwknoprc +############################################################################## +# +# Firewall Knock Operator (fwknop) client rc file. +# +# This file contains user-specific fwknop client configuration default +# and named parameter sets for specific invocations of the fwknop client. +# +# Each section (or stanza) is identified and started by a line in this +# file that contains a single identifier surrounded by square brackets. +# +# The parameters within the stanza typicaly match corresponding client +# command-line parameters. +# +# The first one should always be `[default]' as it defines the global +# default settings for the user. These override the program defaults +# for these parameter. If a named stanza is used, its entries will +# override any of the default. Command-line options will trump them +# all. +# +# Subsequent stanzas will have only the overriding and destination +# specific parameters. +# +# Lines starting with `#' and empty lines are ignored. +# +# See the fwknop.8 man page for a complete list of valid parameters +# and their values. +# +############################################################################## +# +# We start with the 'default' stanza. Uncomment and edit for your +# preferences. The client will use its build-in default for those items +# that are commented out. +# +[default] + +#DIGEST_TYPE sha256 +#FW_TIMEOUT 30 +#SPA_SERVER_PORT 62201 +#SPA_SERVER_PROTO udp +#ALLOW_IP +#SPOOF_USER +#SPOOF_SOURCE_IP +#TIME_OFFSET 0 +#USE_GPG N +#GPG_HOMEDIR /path/to/.gnupg +#GPG_SIGNER +#GPG_RECIPIENT +KEY_BASE64 w8KMBQlKf4UCARNQmi25DPxw4xAUaRs21nf8nbRNzUo= + +# User-provided named stanzas: + +# Example for a destination server of 192.168.1.20 to open access to +# SSH for an IP that is resoved exteranlly, and one with a NAT request +# for a specific source IP that maps port 8088 on the server +# to port 88 on 192.168.1.55 with timeout. +# +#[myssh] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/22 +#ALLOW_IP resolve +# +#[mynatreq] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/8088 +#ALLOW_IP 10.21.2.6 +#NAT_ACCESS 192.168.1.55,88 +#CLIENT_TIMEOUT 60 +# + +###EOF### diff --git a/test/conf/fwknoprc_with_default_key b/test/conf/fwknoprc_with_default_key new file mode 100644 index 00000000..e823f0b3 --- /dev/null +++ b/test/conf/fwknoprc_with_default_key @@ -0,0 +1,71 @@ +# .fwknoprc +############################################################################## +# +# Firewall Knock Operator (fwknop) client rc file. +# +# This file contains user-specific fwknop client configuration default +# and named parameter sets for specific invocations of the fwknop client. +# +# Each section (or stanza) is identified and started by a line in this +# file that contains a single identifier surrounded by square brackets. +# +# The parameters within the stanza typicaly match corresponding client +# command-line parameters. +# +# The first one should always be `[default]' as it defines the global +# default settings for the user. These override the program defaults +# for these parameter. If a named stanza is used, its entries will +# override any of the default. Command-line options will trump them +# all. +# +# Subsequent stanzas will have only the overriding and destination +# specific parameters. +# +# Lines starting with `#' and empty lines are ignored. +# +# See the fwknop.8 man page for a complete list of valid parameters +# and their values. +# +############################################################################## +# +# We start with the 'default' stanza. Uncomment and edit for your +# preferences. The client will use its build-in default for those items +# that are commented out. +# +[default] + +#DIGEST_TYPE sha256 +#FW_TIMEOUT 30 +#SPA_SERVER_PORT 62201 +#SPA_SERVER_PROTO udp +#ALLOW_IP +#SPOOF_USER +#SPOOF_SOURCE_IP +#TIME_OFFSET 0 +#USE_GPG N +#GPG_HOMEDIR /path/to/.gnupg +#GPG_SIGNER +#GPG_RECIPIENT +KEY fwknoptest + +# User-provided named stanzas: + +# Example for a destination server of 192.168.1.20 to open access to +# SSH for an IP that is resoved exteranlly, and one with a NAT request +# for a specific source IP that maps port 8088 on the server +# to port 88 on 192.168.1.55 with timeout. +# +#[myssh] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/22 +#ALLOW_IP resolve +# +#[mynatreq] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/8088 +#ALLOW_IP 10.21.2.6 +#NAT_ACCESS 192.168.1.55,88 +#CLIENT_TIMEOUT 60 +# + +###EOF### diff --git a/test/conf/fwknoprc_with_named_key b/test/conf/fwknoprc_with_named_key new file mode 100644 index 00000000..5bc12f33 --- /dev/null +++ b/test/conf/fwknoprc_with_named_key @@ -0,0 +1,73 @@ +# .fwknoprc +############################################################################## +# +# Firewall Knock Operator (fwknop) client rc file. +# +# This file contains user-specific fwknop client configuration default +# and named parameter sets for specific invocations of the fwknop client. +# +# Each section (or stanza) is identified and started by a line in this +# file that contains a single identifier surrounded by square brackets. +# +# The parameters within the stanza typicaly match corresponding client +# command-line parameters. +# +# The first one should always be `[default]' as it defines the global +# default settings for the user. These override the program defaults +# for these parameter. If a named stanza is used, its entries will +# override any of the default. Command-line options will trump them +# all. +# +# Subsequent stanzas will have only the overriding and destination +# specific parameters. +# +# Lines starting with `#' and empty lines are ignored. +# +# See the fwknop.8 man page for a complete list of valid parameters +# and their values. +# +############################################################################## +# +# We start with the 'default' stanza. Uncomment and edit for your +# preferences. The client will use its build-in default for those items +# that are commented out. +# +[default] + +#DIGEST_TYPE sha256 +#FW_TIMEOUT 30 +#SPA_SERVER_PORT 62201 +#SPA_SERVER_PROTO udp +#ALLOW_IP +#SPOOF_USER +#SPOOF_SOURCE_IP +#TIME_OFFSET 0 +#USE_GPG N +#GPG_HOMEDIR /path/to/.gnupg +#GPG_SIGNER +#GPG_RECIPIENT + +# User-provided named stanzas: + +# Example for a destination server of 192.168.1.20 to open access to +# SSH for an IP that is resoved exteranlly, and one with a NAT request +# for a specific source IP that maps port 8088 on the server +# to port 88 on 192.168.1.55 with timeout. +# +#[myssh] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/22 +#ALLOW_IP resolve +# +#[mynatreq] +#SPA_SERVER 192.168.1.20 +#ACCESS tcp/8088 +#ALLOW_IP 10.21.2.6 +#NAT_ACCESS 192.168.1.55,88 +#CLIENT_TIMEOUT 60 +# + +[testssh] +KEY fwknoptest; + +###EOF### diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index ef0ec8ca..9895fcb9 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -23,6 +23,7 @@ my $gpg_client_home_dir = "$conf_dir/client-gpg"; my $nat_conf = "$conf_dir/nat_fwknopd.conf"; my $default_conf = "$conf_dir/default_fwknopd.conf"; my $default_access_conf = "$conf_dir/default_access.conf"; +my $base64_key_access_conf = "$conf_dir/base64_key_access.conf"; my $ecb_mode_access_conf = "$conf_dir/ecb_mode_access.conf"; my $ctr_mode_access_conf = "$conf_dir/ctr_mode_access.conf"; my $cfb_mode_access_conf = "$conf_dir/cfb_mode_access.conf"; @@ -36,6 +37,10 @@ my $force_nat_access_conf = "$conf_dir/force_nat_access.conf"; my $gpg_access_conf = "$conf_dir/gpg_access.conf"; my $default_digest_file = "$run_dir/digest.cache"; my $default_pid_file = "$run_dir/fwknopd.pid"; +my $tmp_rc_file = "$run_dir/fwknoprc"; +my $rc_file_default_key = "$conf_dir/fwknoprc_with_default_key"; +my $rc_file_default_base64_key = "$conf_dir/fwknoprc_with_default_base64_key"; +my $rc_file_named_key = "$conf_dir/fwknoprc_with_named_key"; my $open_ports_access_conf = "$conf_dir/open_ports_access.conf"; my $multi_gpg_access_conf = "$conf_dir/multi_gpg_access.conf"; my $multi_stanzas_access_conf = "$conf_dir/multi_stanzas_access.conf"; @@ -145,11 +150,20 @@ my $default_client_args = "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . "$fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip --get-key " . "$local_key_file --no-save-args --verbose --verbose"; +my $default_client_args_no_get_key = "LD_LIBRARY_PATH=$lib_dir " . + "$valgrind_str $fwknopCmd -A tcp/22 -a $fake_ip -D $loopback_ip " . + "--no-save-args --verbose --verbose"; + my $default_client_gpg_args = "$default_client_args " . "--gpg-recipient-key $gpg_server_key " . "--gpg-signer-key $gpg_client_key " . "--gpg-home-dir $gpg_client_home_dir"; +my $default_client_gpg_args_no_get_key = "$default_client_args_no_get_key " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir"; + my $default_server_conf_args = "-c $default_conf -a $default_access_conf " . "-d $default_digest_file -p $default_pid_file"; @@ -612,6 +626,82 @@ my @tests = ( 'fw_rule_removed' => $NEW_RULE_REMOVED, 'fatal' => $NO }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'create rc file (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_args --rc-file $tmp_rc_file", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'basic operations', + 'subcategory' => 'client', + 'detail' => "rc file created", + 'err_msg' => "rc file $tmp_rc_file does not exist", + 'function' => \&rc_file_exists, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file default key (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_args_no_get_key " . + "--rc-file $rc_file_default_key", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file base64 key (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_args_no_get_key " . + "--rc-file $rc_file_default_base64_key", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd -c $default_conf -a $base64_key_access_conf " . + "-d $default_digest_file -p $default_pid_file $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file named key (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_args_no_get_key " . + "--rc-file $rc_file_named_key -n testssh", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'Rijndael SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file invalid stanza (tcp/22 ssh)', + 'err_msg' => 'SPA packet generated/accepted', + 'function' => \&generic_exec, + 'cmdline' => "$default_client_args_no_get_key " . + "--rc-file $rc_file_named_key -n invalidstanza", + 'positive_output_matches' => [qr/Named\sconfiguration.*not\sfound/], + 'fatal' => $NO + }, + { 'category' => 'Rijndael SPA', 'subcategory' => 'client+server', @@ -1201,6 +1291,32 @@ my @tests = ( 'fw_rule_removed' => $NEW_RULE_REMOVED, 'fatal' => $NO }, + { + 'category' => 'GnuPG (GPG) SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file default key (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_gpg_args_no_get_key " . + "--rc-file $rc_file_default_key", + 'fwknopd_cmdline' => $default_server_gpg_args, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'GnuPG (GPG) SPA', + 'subcategory' => 'client+server', + 'detail' => 'rc file named key (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_gpg_args_no_get_key " . + "--rc-file $rc_file_named_key -n testssh", + 'fwknopd_cmdline' => $default_server_gpg_args, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, { 'category' => 'GnuPG (GPG) SPA', 'subcategory' => 'client+server', @@ -1683,6 +1799,18 @@ sub spa_cycle() { $rv = 0 if $fw_rule_removed; } + if ($test_hr->{'client_positive_output_matches'}) { + $rv = 0 unless &file_find_regex( + $test_hr->{'client_positive_output_matches'}, + $current_test_file); + } + + if ($test_hr->{'client_negative_output_matches'}) { + $rv = 0 if &file_find_regex( + $test_hr->{'client_negative_output_matches'}, + $current_test_file); + } + if ($test_hr->{'server_positive_output_matches'}) { $rv = 0 unless &file_find_regex( $test_hr->{'server_positive_output_matches'}, @@ -2256,6 +2384,24 @@ sub send_packets() { return; } +sub rc_file_exists() { + my $test_hr = shift; + + my $rv = 1; + + if (-e $tmp_rc_file) { + $rv = 0 unless &file_find_regex([qr/This\sfile\scontains/], + $tmp_rc_file); + } else { + &write_test_file("[-] $tmp_rc_file does not exist.\n", + $current_test_file); + $rv = 0; + } + + return $rv; +} + + sub generic_exec() { my $test_hr = shift; @@ -2623,6 +2769,9 @@ sub init() { if (-e "$output_dir/init") { unlink "$output_dir/init" or die $!; } + if (-e $tmp_rc_file) { + unlink $tmp_rc_file or die $!; + } if (-e $logfile) { unlink $logfile or die $!;