From fd3044012843dfcaa9ab4f9030c70732f29a3b90 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sun, 5 Aug 2012 14:07:42 -0400 Subject: [PATCH 1/5] added Aldan Beaubien for reporting the Morpheus NULL IP problem --- CREDITS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CREDITS b/CREDITS index 5edd47e0..d34fdfdf 100644 --- a/CREDITS +++ b/CREDITS @@ -36,3 +36,8 @@ 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. From fbdae500641b4ab46bc54dbf2e509eae2625dc44 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Wed, 8 Aug 2012 21:27:33 -0400 Subject: [PATCH 2/5] added Geoff Carstairs for the FORCE_NAT idea --- CREDITS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CREDITS b/CREDITS index d34fdfdf..0e1ba857 100644 --- a/CREDITS +++ b/CREDITS @@ -41,3 +41,10 @@ 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. From c6f3fde5371c1be48d8e1bc7e17dde89e19d02fc Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Fri, 10 Aug 2012 21:43:49 -0400 Subject: [PATCH 3/5] bug fix to implement FLUSH_IPT_AT_INIT and FLUSH_IPT_AT_EXIT functionality --- server/fw_util_iptables.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 From 0af3bd0ee10768f6838aafe9fdc66187e5be9ee4 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Fri, 10 Aug 2012 21:48:02 -0400 Subject: [PATCH 4/5] [server] Added FLUSH_IPFW_AT_INIT and FLUSH_IPFW_AT_EXIT Added FLUSH_IPFW_AT_INIT and FLUSH_IPFW_AT_EXIT for ipfw firewalls to emulate the corresponding functionality that is implemented for iptables firewalls. Bug fix for ipfw firewalls to ensure that if the ipfw expire set is zero, then do not disable this set whenever the FLUSH_IPFW* variables are enabled. These changes were suggested by Jonathan Schulz. --- server/cmd_opts.h | 2 + server/config_init.c | 26 +++++++++- server/fw_util_ipfw.c | 46 ++++++++++------- server/fwknopd.conf | 108 +++++++++++++++++++++++----------------- server/fwknopd_common.h | 4 ++ 5 files changed, 121 insertions(+), 65 deletions(-) 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/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 d5abd74c..3ba797d5 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, From 27ccfe35d36c7ba1d94734fb21a46c77aaf30719 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Fri, 10 Aug 2012 21:52:09 -0400 Subject: [PATCH 5/5] [server] Added GPG_ALLOW_NO_PW variable and associated test suite support 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 help provides additional detail: http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment --- ChangeLog | 23 ++++ Makefile.am | 12 +- server/access.c | 13 ++ server/incoming_spa.c | 2 +- test/conf/client-gpg-no-pw/pubring.gpg | Bin 0 -> 2480 bytes test/conf/client-gpg-no-pw/secring.gpg | Bin 0 -> 1274 bytes test/conf/client-gpg-no-pw/trustdb.gpg | Bin 0 -> 1360 bytes test/conf/gpg_no_pw_access.conf | 7 + test/conf/server-gpg-no-pw/pubring.gpg | Bin 0 -> 2480 bytes test/conf/server-gpg-no-pw/secring.gpg | Bin 0 -> 1276 bytes test/conf/server-gpg-no-pw/trustdb.gpg | Bin 0 -> 1360 bytes test/test-fwknop.pl | 176 +++++++++++++++++++++++++ 12 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 test/conf/client-gpg-no-pw/pubring.gpg create mode 100644 test/conf/client-gpg-no-pw/secring.gpg create mode 100644 test/conf/client-gpg-no-pw/trustdb.gpg create mode 100644 test/conf/gpg_no_pw_access.conf create mode 100644 test/conf/server-gpg-no-pw/pubring.gpg create mode 100644 test/conf/server-gpg-no-pw/secring.gpg create mode 100644 test/conf/server-gpg-no-pw/trustdb.gpg 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 15057d3b..c81fb936 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, @@ -396,6 +399,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) @@ -930,6 +936,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/incoming_spa.c b/server/incoming_spa.c index 7f272484..2703a77c 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -436,7 +436,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 0000000000000000000000000000000000000000..615e12f6ebbba9257d6f5502dce8ceae20ef3686 GIT binary patch literal 2480 zcmbuObD2U|DlBS~tTi2-_nhZFf4%>_fBl~4_x%3*eV%Wz02yqP^cgA*2F#jYJ0$Lt zrvr6Dc9uznfM0`M?SwQh&l&id*Q?ZaIezZHo-T=EzIt0)MFJzrFp!Z!rOhdoM9Qgg zotWOW#a{K4t1_MeM+G#T^oP>?cq%N-fWaKJ_h1JixC(WoaC@n3RYs3T+%x&h- z_n<3VH*%vWA73&@LR|f#yi6WM&qdAwuNKmjkD8aP%ewU?&E?FKr)Z7nu{_bJx|%q> zn1K)_hvTMc{tGaE^3s4bWB6P30 zR&Vdm+N!dS+MeZqd_bUgx6g(yR?6sc?}Y1|1|6GapbxFcH2?vi4xsoCQDRb1z_0bi z&1q9k!s>wKRp#hBjm-Y0NqW^VWgoTqi0rX2QI2woG|WC$^8286QE7bMq*_ms@##(0 z7kzutcj4;SfLh6;&Zqefijorh2AzLzE?jFDQgOlRYJCX4%QJosEWQ`3xUXyLirAO) z;#(472d-u**geJ;;OuHtpNL*)9O7z5N-0sZH32DaC$9i8-+GE&A0#}^RNj#!9oC8q{+ zm9{x`G}9Mm(czY7mV!NO)1Lk+Xg87VC4{3kW^b+M-Ar&L$F(@YtrB~n>1`R$d^C@7 zvoGbib}et)qhDMxOS0oBr6pIA8KKFG58e0780l2S zIC{yf`Mr&^wuqo;!c|yu}o)w8HXA!ue~(&dWsKYfGY#aS44EJ z+xZsE6ZP-YWDT)1QJtQ+?`=;??Cqs&Md~k@!t|Q6T&>tyDbWYA=>`fwWev-WB8gxw zZhk@k8~F(XpGPW+I9WuJHfeWhW%|QR?3#45jgEVt*Mk5m^JbBuW3doJ5%;or_xnye z=szCWgZOfrt_idyD>rFs_RZo!$2ys=ok_Y&G_jB);n!gX3g&vnwPt^t=GLB_&bc+t zb7bSewGHcAfwP>uw{=g&IJz9k$-n++?c%^*AkB?G;bz@vA^QcpJaSCq;X#2O7D=li z%$T9P6Sf}Q;9(x5klC-8Bwb*T8<|mR_i2sz>v_OY~n{7D?dz;tie*dM1^*VCPTgd?Nh!8P)Qo!t#p6!Vw9n~ZQ9Z> zugGzsBf>nBHgc8~pv4#0S8tS@c#hZ?SkDRYqw5*vTio_1u=%-hxk=mvub}ZeKWS0z zT{^7ir>`s&D3sCU`k9~Jzurkb-SA+pLS%27d$-tuZEDXisp7b!$*CT1n=jUNB7Re0 z@as%$HD*k>zf^dGcz1z#V#)@qe6i1fMGx|b`GoJa>GGQ;dcMU3OSROZaLA{s+O>}ChkB?*aSkzHbg-=P40y4(;3Bei!P((mP--!Id}=Ji=*3_8)9uxu%Mem) zMO=KV!^sgcIT_~3wKlZQj@yEB&()`k@(GwN{H{}Ph?MRX#y%Nw!T3&Iu_6Nj`T)193JVv8!1%rgV&$J7I}&Q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e84fd95c7496ab361b7515b661dca267121adbde GIT binary patch literal 1274 zcmVNJ-VC_Yy{r~SxW{XWZg4jLa!REyifJT zjDrt9*2#4faQ|xXzdT$dB358Uy6ucD=7f_ znu9>EVma=1F$*hr@o2N_zer<@_EU`4%w?juJf-FZGal{&*0&6 zU&95)>ov`SgZ8)!^e)I|0L{laF>2!^wMB=oKdG?EhTi!KNW#`=WS?)pv!{mawQvm` zO~4Hxcc-~l_|_jsWj`Ic*6ihs|A->i-=@6LN?Q!d-QcB5o+ktVn%E|S+3al&M-tU8 zZJs`L>SgR}F^QoO*s3#|o0i?NWoUCkN8L)H2f_Gm9)`uT)zhWQrYOF%G7~j$3-cL(5g$!? z5C6y}n45c))f~WnI85~H_c2Wi=W!j20waIUQ%wC%7Y`07$Wza=m+QC&8dPg8BkgLP z`#8n|nW1?XD7^NB4EI(I_6~3>hlv(TyK9zhOR5{a3s7`}=j0D$BbN+A4G zrxG}-e0h>*M;3{Q;A%>$V9G6Y^QJc;DQmu*^BUMwHg}FQT9`?1Y|s_h6jvI;zeBJY z5Az1ugMzhOaDfznrr?2Iv>lHJ%$kwsY`0Ep`_8pqKU`(JlFkc2KSp7LY|z~DRO1JS zASDS0c23S1BLD*h2mU-}qG$nc}hH@eMdv7ShI6YCN*fe25v*<@tCJF@$uuwA8&Jfc%DxBQoK1mTT6yJd+Be^@% z+{;fr8voPt`;PztR1N^oJg()yUu0V6761Sl&|8{HX1St3ewbKB3L;B3+3+)T9g$Yp zxDbV!Z literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..58ec2d6dbf204edd880308798a9b72d7f7d66d15 GIT binary patch literal 1360 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 literal 0 HcmV?d00001 diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index e2e580b4..324f8cb7 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -18,6 +18,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", @@ -31,6 +32,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", @@ -160,6 +162,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_server_conf_args = "-c $cf{'def'} -a $cf{'def_access'} " . "-d $default_digest_file -p $default_pid_file"; @@ -168,6 +174,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; @@ -1183,6 +1194,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',