[server] Added GPG_ALLOW_NO_PW variable and associated test suite support
authorMichael Rash <mbr@cipherdyne.org>
Sat, 11 Aug 2012 01:52:09 +0000 (21:52 -0400)
committerMichael Rash <mbr@cipherdyne.org>
Sat, 11 Aug 2012 02:20:30 +0000 (22:20 -0400)
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

12 files changed:
ChangeLog
Makefile.am
server/access.c
server/incoming_spa.c
test/conf/client-gpg-no-pw/pubring.gpg [new file with mode: 0644]
test/conf/client-gpg-no-pw/secring.gpg [new file with mode: 0644]
test/conf/client-gpg-no-pw/trustdb.gpg [new file with mode: 0644]
test/conf/gpg_no_pw_access.conf [new file with mode: 0644]
test/conf/server-gpg-no-pw/pubring.gpg [new file with mode: 0644]
test/conf/server-gpg-no-pw/secring.gpg [new file with mode: 0644]
test/conf/server-gpg-no-pw/trustdb.gpg [new file with mode: 0644]
test/test-fwknop.pl

index 17c531c..dc0027e 100644 (file)
--- 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
index b682fa3..16dfecb 100644 (file)
@@ -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 \
index 15057d3..c81fb93 100644 (file)
@@ -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);
index 7f27248..2703a77 100644 (file)
@@ -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 (file)
index 0000000..615e12f
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 (file)
index 0000000..e84fd95
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 (file)
index 0000000..58ec2d6
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 (file)
index 0000000..2f6e810
--- /dev/null
@@ -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 (file)
index 0000000..b0aa06e
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 (file)
index 0000000..c3a465a
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 (file)
index 0000000..d5b5b60
Binary files /dev/null and b/test/conf/server-gpg-no-pw/trustdb.gpg differ
index e2e580b..324f8cb 100755 (executable)
@@ -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;
@@ -1184,6 +1195,171 @@ my @tests = (
     },
 
     {
+        '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',
         'detail'   => 'complete cycle (tcp/22 ssh)',