diff --git a/CREDITS b/CREDITS index 5edd47e0..0e1ba857 100644 --- a/CREDITS +++ b/CREDITS @@ -36,3 +36,15 @@ Franck Joncourt Jonathan Schulz - Submitted patches to change HTTP connection type to 'close' for -R mode in the client and fix a bug for recv() calls against returned HTTP data. + +Aldan Beaubien + - Reported an issue with the Morpheus client sending SPA packets with NULL + IP addresses, and code was added to fwknopd to better validate incoming + SPA data as a result of this report. + +Geoff Carstairs + - Suggested a way to redirect valid connection requests to a specific + internal service via NAT, configurable by each stanza in access.conf. + This allows for better access control for multple users requiring access + to multiple internal systems, in a manner that is transparent to the + user. The result was the FORCE_NAT mode. diff --git a/ChangeLog b/ChangeLog index 17c531c5..dc0027ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,16 @@ fwknop-2.0.2 (08//2012): + - [server] For GPG mode, added a new access.conf variable + "GPG_ALLOW_NO_PW" to make it possible to leverage a server-side GPG key + pair that has no associated password. This comes in handy when a system + requires the user to leverage gpg-agent / pinentry which can present a + problem in automated environments as required by the fwknopd server. + Now, it might seem like a problem to remove the passphrase from a GPG + key pair, but it's important to note that simply doing this is little + worse than storing the passphrase in the clear on disk anyway in the + access.conf file. Further, this link helps provide additional detail: + + http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment + - [client] In IP resolution mode (-R) changed HTTP connection type to 'close' since there is no need for connection persistence, and indeed the client expects to just get the IP and the connection to be closed. @@ -7,6 +19,14 @@ fwknop-2.0.2 (08//2012): remote webserver IP resolution mode (-R). Previously IP resolution could fail if HTTP headers were transferred separately from the data (for whatever reason). Jonathan Schulz submitted a patch for this. + - [server] Bug fix to implement FLUSH_IPT_AT_INIT and FLUSH_IPT_AT_EXIT + functionality. These are enabled by default, and now iptables rules + added by fwknopd can be made persistant by setting these variables to + "N" in the fwknopd.conf file (this is not a recommended setting + however). + [server] Added FLUSH_IPFW_AT_INIT and FLUSH_IPFW_AT_EXIT for ipfw + firewalls to emulate the corresponding functionality that is implemented + for iptables firewalls. This was suggested by Jonathan Schulz. - [server] Replay attack bug fix to ensure that an attacker cannot force a replay attack by intercepting an SPA packet and the replaying it with the base64 version of "Salted__" (for Rindael) or the "hQ" prefix (for @@ -36,6 +56,9 @@ fwknop-2.0.2 (08//2012): by 0x10DABF: pcap_capture (pcap_capture.c:226) by 0x10A798: main (fwknopd.c:299) + - [test suite] Added GPG tests for keyrings that have no associated + passphrases. + fwknop-2.0.1 (07/23/2012): - [server] Bug fix where the same encryption key used for two stanzas in the access.conf file would result in access requests that matched the diff --git a/Makefile.am b/Makefile.am index b682fa3d..16dfecb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -109,6 +109,15 @@ EXTRA_DIST = \ test/conf/client-gpg/pubring.gpg \ test/conf/client-gpg/secring.gpg \ test/conf/client-gpg/trustdb.gpg \ + test/conf/client-gpg-no-pw/pubring.gpg \ + test/conf/client-gpg-no-pw/secring.gpg \ + test/conf/client-gpg-no-pw/trustdb.gpg \ + test/conf/server-gpg/pubring.gpg \ + test/conf/server-gpg/secring.gpg \ + test/conf/server-gpg/trustdb.gpg \ + test/conf/server-gpg-no-pw/pubring.gpg \ + test/conf/server-gpg-no-pw/secring.gpg \ + test/conf/server-gpg-no-pw/trustdb.gpg \ test/conf/default_access.conf \ test/conf/default_fwknopd.conf \ test/conf/dual_key_usage_access.conf \ @@ -133,9 +142,6 @@ EXTRA_DIST = \ test/conf/override_fwknopd.conf \ test/conf/require_src_access.conf \ test/conf/require_user_access.conf \ - test/conf/server-gpg/pubring.gpg \ - test/conf/server-gpg/secring.gpg \ - test/conf/server-gpg/trustdb.gpg \ test/conf/subnet_source_match_access.conf \ test/conf/local_nat_fwknopd.conf \ test/hardening-check \ diff --git a/server/access.c b/server/access.c index 2b6e4f12..8af966da 100644 --- a/server/access.c +++ b/server/access.c @@ -46,6 +46,9 @@ static void add_acc_string(char **var, const char *val) { + if(*var != NULL) + free(*var); + if((*var = strdup(val)) == NULL) { log_msg(LOG_ERR, @@ -437,6 +440,9 @@ add_string_list_ent(acc_string_list_t **stlist, const char *str_str) last_stlist->next = new_stlist; } + if(new_stlist->str != NULL) + free(new_stlist->str); + new_stlist->str = strdup(str_str); if(new_stlist->str == NULL) @@ -1044,6 +1050,13 @@ parse_access_file(fko_srv_options_t *opts) } add_acc_string(&(curr_acc->gpg_decrypt_pw), val); } + else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW")) + { + if(curr_acc->gpg_decrypt_pw != NULL && curr_acc->gpg_decrypt_pw[0] != '\0') + free(curr_acc->gpg_decrypt_pw); + + add_acc_string(&(curr_acc->gpg_decrypt_pw), ""); + } else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG")) { add_acc_bool(&(curr_acc->gpg_require_sig), val); diff --git a/server/cmd_opts.h b/server/cmd_opts.h index 68e59bb6..05dc9cac 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -79,6 +79,8 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = { "IPT_SNAT_ACCESS", "IPT_MASQUERADE_ACCESS", #elif FIREWALL_IPFW + "FLUSH_IPFW_AT_INIT", + "FLUSH_IPFW_AT_EXIT", "IPFW_START_RULE_NUM", "IPFW_MAX_RULES", "IPFW_ACTIVE_SET_NUM", diff --git a/server/config_init.c b/server/config_init.c index ef837504..f83ce204 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -43,7 +43,7 @@ range_check(fko_srv_options_t *opts, char *var, char *val, int low, int high) { if (low > atoi(val) || high < atoi(val)) { - fprintf(stderr, "[*] var %s value '%s' not in the range %d-%d", + fprintf(stderr, "[*] var %s value '%s' not in the range %d-%d\n", var, val, low, high); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); } @@ -150,6 +150,19 @@ validate_int_var_ranges(fko_srv_options_t *opts) opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL], 1, RCHK_MAX_IPFW_PURGE_INTERVAL); + /* Make sure the active and expire sets are not identical whenever + * they are non-zero + */ + if((opts->config[CONF_IPFW_ACTIVE_SET_NUM] > 0 + && opts->config[CONF_IPFW_EXPIRE_SET_NUM] > 0) + && (opts->config[CONF_IPFW_ACTIVE_SET_NUM] + == opts->config[CONF_IPFW_EXPIRE_SET_NUM])) + { + fprintf(stderr, + "[*] Cannot set identical ipfw active and expire sets.\n"); + clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); + } + #elif FIREWALL_PF range_check(opts, "PF_EXPIRE_INTERVAL", opts->config[CONF_PF_EXPIRE_INTERVAL], 1, RCHK_MAX_PF_EXPIRE_INTERVAL); @@ -446,6 +459,17 @@ validate_options(fko_srv_options_t *opts) DEF_IPT_MASQUERADE_ACCESS); #elif FIREWALL_IPFW + + /* Flush ipfw rules at init. + */ + if(opts->config[CONF_FLUSH_IPFW_AT_INIT] == NULL) + set_config_entry(opts, CONF_FLUSH_IPFW_AT_INIT, DEF_FLUSH_IPFW_AT_INIT); + + /* Flush ipfw rules at exit. + */ + if(opts->config[CONF_FLUSH_IPFW_AT_EXIT] == NULL) + set_config_entry(opts, CONF_FLUSH_IPFW_AT_EXIT, DEF_FLUSH_IPFW_AT_EXIT); + /* Set IPFW start rule number. */ if(opts->config[CONF_IPFW_START_RULE_NUM] == NULL) diff --git a/server/fw_util_ipfw.c b/server/fw_util_ipfw.c index ae441687..89eb26c4 100644 --- a/server/fw_util_ipfw.c +++ b/server/fw_util_ipfw.c @@ -212,7 +212,8 @@ fw_initialize(const fko_srv_options_t *opts) /* For now, we just call fw_cleanup to start with clean slate. */ - res = fw_cleanup(opts); + if(strncasecmp(opts->config[CONF_FLUSH_IPFW_AT_INIT], "Y", 1) == 0) + res = fw_cleanup(opts); if(res != 0) { @@ -261,26 +262,30 @@ fw_initialize(const fko_srv_options_t *opts) log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); } - /* Make sure our expire set is disabled. - */ - zero_cmd_buffers(); + if(fwc.expire_set_num > 0 + && (strncasecmp(opts->config[CONF_FLUSH_IPFW_AT_INIT], "Y", 1) == 0)) + { + /* Make sure our expire set is disabled. + */ + zero_cmd_buffers(); - snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_DISABLE_SET_ARGS, - fwc.fw_command, - fwc.expire_set_num - ); + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_DISABLE_SET_ARGS, + fwc.fw_command, + fwc.expire_set_num + ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); - if (opts->verbose) - log_msg(LOG_INFO, "fw_initialize() CMD: '%s' (res: %d, err: %s)", - cmd_buf, res, err_buf); + if (opts->verbose) + log_msg(LOG_INFO, "fw_initialize() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); - if(EXTCMD_IS_SUCCESS(res)) - log_msg(LOG_INFO, "Set ipfw expire set %u to disabled.", - fwc.expire_set_num); - else - log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + if(EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_INFO, "Set ipfw expire set %u to disabled.", + fwc.expire_set_num); + else + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + } /* Now read the expire set in case there are existing * rules to track. @@ -351,6 +356,13 @@ fw_cleanup(const fko_srv_options_t *opts) { int res, got_err = 0; + if(strncasecmp(opts->config[CONF_FLUSH_IPFW_AT_EXIT], "N", 1) == 0) + { + if(fwc.rule_map != NULL) + free(fwc.rule_map); + return(0); + } + zero_cmd_buffers(); if(fwc.active_set_num > 0 diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index 0f374336..8e114707 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -454,7 +454,8 @@ fw_initialize(const fko_srv_options_t *opts) /* Flush the chains (just in case) so we can start fresh. */ - delete_all_chains(opts); + if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_INIT], "Y", 1) == 0) + delete_all_chains(opts); /* Now create any configured chains. */ @@ -470,6 +471,9 @@ fw_initialize(const fko_srv_options_t *opts) int fw_cleanup(const fko_srv_options_t *opts) { + if(strncasecmp(opts->config[CONF_FLUSH_IPT_AT_EXIT], "N", 1) == 0) + return(0); + delete_all_chains(opts); return(0); } @@ -650,7 +654,7 @@ process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_ nat_port = atoi(ndx+1); } } - + if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG) { /* Need to add an ACCEPT rule into the INPUT chain diff --git a/server/fwknopd.conf b/server/fwknopd.conf index 2b6e2685..6aed85e6 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -77,53 +77,11 @@ # the pcap loop. The default is 100000 microseconds, or 1/10th of a second. #PCAP_LOOP_SLEEP 100000; -# Allow SPA clients to request access to services through an iptables -# firewall instead of just to it (i.e. access through the FWKNOP_FORWARD -# chain instead of the INPUT chain). -# -#ENABLE_IPT_FORWARDING N; - -# Allow SPA clients to request access to a local socket via NAT. This still -# puts an ACCEPT rule into the FWKNOP_INPUT chain, but a different port is -# translated via DNAT rules to the real one. So, the user would do -# "ssh -p " to access the local service (see the --NAT-local and -# --NAT-rand-port on the fwknop client command line). -# -#ENABLE_IPT_LOCAL_NAT Y; - -# By default, if forwarding access is enabled (see the ENABLE_IPT_FORWARDING -# variable above), then fwknop creates DNAT rules for incoming connections, -# but does not also complement these rules with SNAT rules at the same time. -# In some situations, internal systems may not have a route back out for the -# source address of the incoming connection, so it is necessary to also -# apply SNAT rules so that the internal systems see the IP of the internal -# interface where fwknopd is running. This functionality is only enabled -# when ENABLE_IPT_SNAT is set to "Y", and by default SNAT rules are built -# with the MASQUERADE target (since then the internal IP does not have to be -# defined here in the fwknop.conf file), but if you want fwknopd to use the -# SNAT target then also defined an IP address with the SNAT_TRANSLATE_IP -# variable. -# -#ENABLE_IPT_SNAT N; -#SNAT_TRANSLATE_IP __CHANGEME__; - -# Add ACCEPT rules to the FWKNOP_OUTPUT chain. This is usually only useful -# if there are no state tracking rules to allow connection responses out and -# the OUTPUT chain has a default-drop stance. -# -#ENABLE_IPT_OUTPUT N; - # Specify the the maximum number of bytes to sniff per frame - 1500 # is a good default # #MAX_SNIFF_BYTES 1500; -# Flush all existing rules in the fwknop chains at fwknop start time and/or -# exit time. They default to Y and it is recommended setting for both. -# -#FLUSH_IPT_AT_INIT Y; -#FLUSH_IPT_AT_EXIT Y; - # If GPG keys are used instead of a Rijndael symmetric key, this is # the default GPG keys directory. Note that each access block in # fwknop access.conf can specify its own GPG directory to override @@ -144,7 +102,7 @@ # accept incoming TCP request. This server only accepts the # request. It does not otherwise communicate. This is only to allow the # incoming SPA over TCP packet which is detected via PCAP. The connection -# is closed after 1 second regardless. +# is closed after 1 second regardless. # Note that fwknopd still only gets its data via pcap, so the filter # defined by PCAP_FILTER needs to be updated to include this TCP port. # @@ -209,7 +167,50 @@ ############################################################################## # Parameters specific to iptables: + +# Flush all existing rules in the fwknop chains at fwknop start time and/or +# exit time. They default to Y and it is a recommended setting for both. # +#FLUSH_IPT_AT_INIT Y; +#FLUSH_IPT_AT_EXIT Y; +# + +# Allow SPA clients to request access to services through an iptables +# firewall instead of just to it (i.e. access through the FWKNOP_FORWARD +# chain instead of the INPUT chain). +# +#ENABLE_IPT_FORWARDING N; + +# Allow SPA clients to request access to a local socket via NAT. This still +# puts an ACCEPT rule into the FWKNOP_INPUT chain, but a different port is +# translated via DNAT rules to the real one. So, the user would do +# "ssh -p " to access the local service (see the --NAT-local and +# --NAT-rand-port on the fwknop client command line). +# +#ENABLE_IPT_LOCAL_NAT Y; + +# By default, if forwarding access is enabled (see the ENABLE_IPT_FORWARDING +# variable above), then fwknop creates DNAT rules for incoming connections, +# but does not also complement these rules with SNAT rules at the same time. +# In some situations, internal systems may not have a route back out for the +# source address of the incoming connection, so it is necessary to also +# apply SNAT rules so that the internal systems see the IP of the internal +# interface where fwknopd is running. This functionality is only enabled +# when ENABLE_IPT_SNAT is set to "Y", and by default SNAT rules are built +# with the MASQUERADE target (since then the internal IP does not have to be +# defined here in the fwknop.conf file), but if you want fwknopd to use the +# SNAT target then also defined an IP address with the SNAT_TRANSLATE_IP +# variable. +# +#ENABLE_IPT_SNAT N; +#SNAT_TRANSLATE_IP __CHANGEME__; + +# Add ACCEPT rules to the FWKNOP_OUTPUT chain. This is usually only useful +# if there are no state tracking rules to allow connection responses out and +# the OUTPUT chain has a default-drop stance. +# +#ENABLE_IPT_OUTPUT N; + # fwknopd adds allow rules to a custom iptables chain "FWKNOP_INPUT". # This chain is called from the INPUT chain, and by default no other # iptables chains are used. However, additional chains can be added @@ -278,17 +279,30 @@ # #IPFW_MAX_RULES 1000; +# Flush all existing rules in the fwknop ipfw sets at fwknop start time and/or +# exit time. They default to Y and it is a recommended setting for both. +# +#FLUSH_IPFW_AT_INIT Y; +#FLUSH_IPFW_AT_EXIT Y; + # This variable defines the rule set fwknopd uses for active rules. By -# default, it is set 0, but can be set to any number between 0 and 31 in -# case you want to keep fwknopd generated rules segregated from the default -# ruleset. +# default, it is set 1 and fwknopd assumes that it has full control over this +# set. That is, fwknopd routinely creates and deletes rules in this set, and +# the entire set itself is also created/deleted during routine operations. +# You have some measure of control over whether the entire set is deleted at +# init/exit with the FLUSH_IPFW_AT_INIT and FLUSH_IPFW_AT_EXIT, but in general +# it is recommended to leave these variables set to the default "Y" setting. # #IPFW_ACTIVE_SET_NUM 1; # This variable defines the rule set that will be used to store expired rules # that still have a dynamic rule associated to them. That set will be disabled # by fwknop and should not be enabled while fwknop is running. Not used when -# ipfw isn't using dynamic rules. +# ipfw isn't using dynamic rules. By default, it is set 2, but can be anything +# in the range 1-31 except that it shouldn't be the same as +# IPFW_ACTIVE_SET_NUM. Note that fwknopd disables this set through routine +# operations according to the FLUSH_IPFW_AT_INIT and FLUSH_IPFW_AT_EXIT +# variables. # #IPFW_EXPIRE_SET_NUM 2; diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index c9cde488..ed19b44a 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -121,6 +121,8 @@ */ #elif FIREWALL_IPFW + #define DEF_FLUSH_IPFW_AT_INIT "Y" + #define DEF_FLUSH_IPFW_AT_EXIT "Y" #define DEF_IPFW_START_RULE_NUM "10000" #define DEF_IPFW_MAX_RULES "1000" #define DEF_IPFW_ACTIVE_SET_NUM "1" @@ -200,6 +202,8 @@ enum { CONF_IPT_SNAT_ACCESS, CONF_IPT_MASQUERADE_ACCESS, #elif FIREWALL_IPFW + CONF_FLUSH_IPFW_AT_INIT, + CONF_FLUSH_IPFW_AT_EXIT, CONF_IPFW_START_RULE_NUM, CONF_IPFW_MAX_RULES, CONF_IPFW_ACTIVE_SET_NUM, diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 8aeaec12..6cbf66d4 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -440,7 +440,7 @@ incoming_spa(fko_srv_options_t *opts) else { log_msg(LOG_ERR, - "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages", + "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages, set GPG_ALLOW_NO_PW", stanza_num ); acc = acc->next; diff --git a/test/conf/client-gpg-no-pw/pubring.gpg b/test/conf/client-gpg-no-pw/pubring.gpg new file mode 100644 index 00000000..615e12f6 Binary files /dev/null and b/test/conf/client-gpg-no-pw/pubring.gpg differ diff --git a/test/conf/client-gpg-no-pw/secring.gpg b/test/conf/client-gpg-no-pw/secring.gpg new file mode 100644 index 00000000..e84fd95c Binary files /dev/null and b/test/conf/client-gpg-no-pw/secring.gpg differ diff --git a/test/conf/client-gpg-no-pw/trustdb.gpg b/test/conf/client-gpg-no-pw/trustdb.gpg new file mode 100644 index 00000000..58ec2d6d Binary files /dev/null and b/test/conf/client-gpg-no-pw/trustdb.gpg differ diff --git a/test/conf/gpg_no_pw_access.conf b/test/conf/gpg_no_pw_access.conf new file mode 100644 index 00000000..2f6e810f --- /dev/null +++ b/test/conf/gpg_no_pw_access.conf @@ -0,0 +1,7 @@ +SOURCE: ANY; +KEY: fwknoptest; +FW_ACCESS_TIMEOUT: 3; +GPG_HOME_DIR: conf/server-gpg-no-pw; +GPG_DECRYPT_ID: 361BBAD4; +GPG_ALLOW_NO_PW: Y; +GPG_REMOTE_ID: 6A3FAD56; diff --git a/test/conf/server-gpg-no-pw/pubring.gpg b/test/conf/server-gpg-no-pw/pubring.gpg new file mode 100644 index 00000000..b0aa06e8 Binary files /dev/null and b/test/conf/server-gpg-no-pw/pubring.gpg differ diff --git a/test/conf/server-gpg-no-pw/secring.gpg b/test/conf/server-gpg-no-pw/secring.gpg new file mode 100644 index 00000000..c3a465a5 Binary files /dev/null and b/test/conf/server-gpg-no-pw/secring.gpg differ diff --git a/test/conf/server-gpg-no-pw/trustdb.gpg b/test/conf/server-gpg-no-pw/trustdb.gpg new file mode 100644 index 00000000..d5b5b60c Binary files /dev/null and b/test/conf/server-gpg-no-pw/trustdb.gpg differ diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index abaff442..0669d056 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -19,6 +19,7 @@ my $configure_path = '../configure'; my $cmd_out_tmp = 'cmd.out'; my $server_cmd_tmp = 'server_cmd.out'; my $gpg_client_home_dir = "$conf_dir/client-gpg"; +my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw"; my %cf = ( 'nat' => "$conf_dir/nat_fwknopd.conf", @@ -33,6 +34,7 @@ my %cf = ( 'local_nat' => "$conf_dir/local_nat_fwknopd.conf", 'dual_key_access' => "$conf_dir/dual_key_usage_access.conf", 'gpg_access' => "$conf_dir/gpg_access.conf", + 'gpg_no_pw_access' => "$conf_dir/gpg_no_pw_access.conf", 'open_ports_access' => "$conf_dir/open_ports_access.conf", 'multi_gpg_access' => "$conf_dir/multi_gpg_access.conf", 'multi_stanza_access' => "$conf_dir/multi_stanzas_access.conf", @@ -196,6 +198,10 @@ my $default_client_gpg_args = "$default_client_args " . "--gpg-signer-key $gpg_client_key " . "--gpg-home-dir $gpg_client_home_dir"; +my $default_client_gpg_args_no_homedir = "$default_client_args " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key "; + 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 " . @@ -209,6 +215,11 @@ my $default_server_gpg_args = "LD_LIBRARY_PATH=$lib_dir " . "-a $cf{'gpg_access'} $intf_str " . "-d $default_digest_file -p $default_pid_file"; +my $default_server_gpg_args_no_pw = "LD_LIBRARY_PATH=$lib_dir " . + "$valgrind_str $fwknopdCmd -c $cf{'def'} " . + "-a $cf{'gpg_no_pw_access'} $intf_str " . + "-d $default_digest_file -p $default_pid_file"; + ### point the compiled binaries at the local libary path ### instead of any installed libfko instance $ENV{'LD_LIBRARY_PATH'} = $lib_dir; @@ -1589,6 +1600,171 @@ my @tests = ( 'fatal' => $NO }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'multi gpg-IDs (tcp/22 ssh)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir " . + "$valgrind_str $fwknopdCmd -c $cf{'def'} " . + "-a $cf{'multi_gpg_access'} $intf_str " . + "-d $default_digest_file -p $default_pid_file", + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/23 telnet)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/23 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (tcp/9418 git)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A tcp/9418 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'complete cycle (udp/53 dns)', + 'err_msg' => 'could not complete SPA cycle', + 'function' => \&spa_cycle, + 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopCmd -A udp/53 -a $fake_ip -D $loopback_ip --get-key " . + "$local_key_file --verbose --verbose " . + "--gpg-recipient-key $gpg_server_key " . + "--gpg-signer-key $gpg_client_key " . + "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fw_rule_created' => $NEW_RULE_REQUIRED, + 'fw_rule_removed' => $NEW_RULE_REMOVED, + 'fatal' => $NO + }, + + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'replay attack detection', + 'err_msg' => 'could not detect replay attack', + 'function' => \&replay_detection, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'replay_positive_output_matches' => [qr/Replay\sdetected\sfrom\ssource\sIP/], + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'replay detection (GnuPG prefix)', + 'err_msg' => 'could not detect replay attack', + 'function' => \&replay_detection, + 'pkt_prefix' => 'hQ', + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " . + "$fwknopdCmd $default_server_conf_args $intf_str", + 'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/], + 'fatal' => $NO + }, + + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'non-base64 altered SPA data', + 'err_msg' => 'allowed improper SPA data', + 'function' => \&altered_non_base64_spa_data, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'base64 altered SPA data', + 'err_msg' => 'allowed improper SPA data', + 'function' => \&altered_base64_spa_data, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'appended data to SPA pkt', + 'err_msg' => 'allowed improper SPA data', + 'function' => \&appended_spa_data, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'prepended data to SPA pkt', + 'err_msg' => 'allowed improper SPA data', + 'function' => \&prepended_spa_data, + 'cmdline' => "$default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fatal' => $NO + }, + { + 'category' => 'GPG (no pw) SPA', + 'subcategory' => 'client+server', + 'detail' => 'spoof username (tcp/22 ssh)', + 'err_msg' => 'could not spoof username', + 'function' => \&spoof_username, + 'cmdline' => "SPOOF_USER=$spoof_user $default_client_gpg_args_no_homedir " + . "--gpg-home-dir $gpg_client_home_dir_no_pw", + 'fwknopd_cmdline' => $default_server_gpg_args_no_pw, + 'fatal' => $NO + }, + { 'category' => 'GnuPG (GPG) SPA', 'subcategory' => 'client+server',