merged master 2.0.3 changes
authorMichael Rash <mbr@cipherdyne.org>
Sat, 1 Sep 2012 01:43:55 +0000 (21:43 -0400)
committerMichael Rash <mbr@cipherdyne.org>
Sat, 1 Sep 2012 01:43:55 +0000 (21:43 -0400)
13 files changed:
1  2 
client/config_init.c
client/fwknop.c
client/utils.c
client/utils.h
common/common.h
configure.ac
lib/fko_message.h
server/access.c
server/fwknopd_common.h
server/incoming_spa.c
server/utils.c
server/utils.h
test/test-fwknop.pl

@@@ -470,46 -414,46 +477,53 @@@ process_rc(fko_cli_options_t *options
  
      char    *ndx, *emark, *homedir;
  
 +    memset(rcfile, 0x0, MAX_PATH_LEN);
 +
 +    if(options->rc_file[0] == 0x0)
 +    {
  #ifdef WIN32
 -    homedir = getenv("USERPROFILE");
 +        homedir = getenv("USERPROFILE");
  #else
 -    homedir = getenv("HOME");
 +        homedir = getenv("HOME");
  #endif
  
 -    if(homedir == NULL)
 -    {
 -        fprintf(stderr, "Warning: Unable to determine HOME directory.\n"
 -            " No .fwknoprc file processed.\n");
 -        return;
 -    }
 +        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);
  
 -    strlcpy(rcfile, homedir, MAX_PATH_LEN);
 +        rcf_offset = strlen(rcfile);
  
 -    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;
 +        }
  
 -    /* 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))
 +        rcfile[rcf_offset] = PATH_SEP;
 +        strlcat(rcfile, ".fwknoprc", MAX_PATH_LEN);
 +    }
 +    else
      {
 -        fprintf(stderr, "Warning: Path to .fwknoprc file is too long.\n"
 -            " No .fwknoprc file processed.\n");
 -        return;
 +        strlcpy(rcfile, options->rc_file, MAX_PATH_LEN);
      }
  
 -    rcfile[rcf_offset] = PATH_SEP;
 -    strlcat(rcfile, ".fwknoprc", MAX_PATH_LEN);
 -
+     /* Check rc file permissions - if anything other than user read/write,
+      * then don't process it.  This change was made to help ensure that the
+      * client consumes a proper rc file with strict permissions set (thanks
+      * to Fernando Arnaboldi from IOActive for pointing this out).
+     */
+     verify_file_perms_ownership(rcfile);
      /* Open the rc file for reading, if it does not exist, then create
       * an initial .fwknoprc file with defaults and go on.
      */
diff --cc client/fwknop.c
@@@ -48,9 -47,9 +48,11 @@@ static int set_message_type(fko_ctx_t c
  static int set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options);
  static int get_rand_port(fko_ctx_t ctx);
  int resolve_ip_http(fko_cli_options_t *options);
 +static void clean_exit(fko_ctx_t ctx, fko_cli_options_t *opts,
 +    unsigned int exit_status);
  
+ #define MAX_CMDLINE_ARGS    50  /* should be way more than enough */
  int
  main(int argc, char **argv)
  {
@@@ -640,6 -566,6 +642,7 @@@ show_last_command(void
                  args_save_file);
              exit(EXIT_FAILURE);
          }
++        verify_file_perms_ownership(args_save_file);
          if ((fgets(args_str, MAX_LINE_LEN, args_file_ptr)) != NULL) {
              printf("Last fwknop client command line: %s", args_str);
          } else {
diff --cc client/utils.c
@@@ -68,25 -67,69 +68,90 @@@ hex_dump(const unsigned char *data, con
      }
  }
  
 +/* 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; i<len; i++)
 +    {
 +        if(!(isalnum(buf[i]) || buf[i] == '/' || buf[i] == '+' || buf[i] == '='))
 +        {
 +            rv = 0;
 +            break;
 +        }
 +    }
 +
 +    return rv;
 +}
 +
+ int
+ set_file_perms(const char *file)
+ {
+     int res = 0;
+     res = chmod(file, S_IRUSR | S_IWUSR);
+     if(res != 0)
+     {
+         fprintf(stderr,
+             "[-] unable to chmod file %s to user read/write (0600, -rw-------): %s\n",
+             file,
+             strerror(errno)
+         );
+     }
+     return res;
+ }
+ int
+ verify_file_perms_ownership(const char *file)
+ {
+ #if HAVE_STAT
+     struct stat st;
+     /* Every file that the fwknop client deals with should be owned
+      * by the user and permissions set to 600 (user read/write)
+     */
+     if((stat(file, &st)) != 0)
+     {
+         fprintf(stderr, "[-] unable to run stat() against file: %s: %s\n",
+             file, strerror(errno));
+         exit(EXIT_FAILURE);
+     }
+     /* Make sure it is a regular file or symbolic link
+     */
+     if(S_ISREG(st.st_mode) != 1 && S_ISLNK(st.st_mode) != 1)
+     {
+         fprintf(stderr,
+             "[-] file: %s is not a regular file or symbolic link.\n",
+             file
+         );
+         return 0;
+     }
+     if((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != (S_IRUSR|S_IWUSR))
+     {
+         fprintf(stderr,
+             "[-] file: %s permissions should only be user read/write (0600, -rw-------)\n",
+             file
+         );
+         return 0;
+     }
+     if(st.st_uid != getuid())
+     {
+         fprintf(stderr, "[-] file: %s not owned by current effective user id.\n",
+             file);
+         return 0;
+     }
+ #endif
+     return 1;
+ }
  /***EOF***/
diff --cc client/utils.h
  /* Prototypes
  */
  void hex_dump(const unsigned char *data, const int size);
 +int is_base64(const unsigned char *buf, const unsigned short int len);
+ int set_file_perms(const char *file);
+ int verify_file_perms_ownership(const char *file);
  size_t strlcat(char *dst, const char *src, size_t siz);
  size_t strlcpy(char *dst, const char *src, size_t siz);
  
diff --cc common/common.h
@@@ -102,8 -102,8 +102,6 @@@ enum 
  #define DEFAULT_NAT_PORT    55000
  #define MIN_HIGH_PORT       10000  /* sensible minimum for SPA dest port */
  #define MAX_PORT            65535
--#define MAX_PORT_STR_LEN    6
--#define MAX_PROTO_STR_LEN   6
  #define MAX_SERVER_STR_LEN  50
  
  #define MAX_LINE_LEN        1024
diff --cc configure.ac
Simple merge
@@@ -32,6 -32,9 +32,9 @@@
  #ifndef FKO_MESSAGE_H
  #define FKO_MESSAGE_H 1
  
 -#define MAX_PROTO_STR_LEN   4  /* tcp, udp, icmp for now */
 -#define MAX_PORT_STR_LEN    5
++#define MAX_PROTO_STR_LEN   5  /* tcp, udp, icmp for now */
++#define MAX_PORT_STR_LEN    6
  /* SPA message format validation functions.
  */
  int validate_cmd_msg(const char *msg);
diff --cc server/access.c
@@@ -213,8 -168,8 +213,8 @@@ add_acc_force_nat(fko_srv_options_t *op
  /* Take an IP or Subnet/Mask and convert it to mask for later
   * comparisons of incoming source IPs against this mask.
  */
- static void
+ static int
 -add_source_mask(acc_stanza_t *acc, const char *ip)
 +add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip)
  {
      char                *ndx;
      char                ip_str[MAX_IPV4_STR_LEN] = {0};
          if(inet_aton(ip_str, &in) == 0)
          {
              log_msg(LOG_ERR,
 -                "Error parsing IP to int for: %s", ip_str
 +                "Fatal error parsing IP to int for: %s", ip_str
              );
-             clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
+             free(new_sle);
+             new_sle = NULL;
+             return 0;
          }
  
          /* Store our mask converted from CIDR to a 32-bit value.
  
  /* Expand the access SOURCE string to a list of masks.
  */
- void
+ static int
 -expand_acc_source(acc_stanza_t *acc)
 +expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc)
  {
      char           *ndx, *start;
-     char            buf[32];
+     char            buf[ACCESS_BUF_LEN];
+     int             res = 1;
  
      start = acc->source;
  
              while(isspace(*start))
                  start++;
  
+             if(((ndx-start)+1) >= ACCESS_BUF_LEN)
+                 return 0;
              strlcpy(buf, start, (ndx-start)+1);
-             add_source_mask(opts, acc, buf);
 -            res = add_source_mask(acc, buf);
++
++            res = add_source_mask(opts, acc, buf);
+             if(res == 0)
+                 return res;
++
              start = ndx+1;
          }
      }
      while(isspace(*start))
          start++;
  
+     if(((ndx-start)+1) >= ACCESS_BUF_LEN)
+         return 0;
      strlcpy(buf, start, (ndx-start)+1);
-     add_source_mask(opts, acc, buf);
 -    res = add_source_mask(acc, buf);
++
++    res = add_source_mask(opts, acc, buf);
+     return res;
  }
  
  static int
@@@ -652,7 -635,11 +692,11 @@@ expand_acc_ent_lists(fko_srv_options_t 
      {
          /* Expand the source string to 32-bit integer masks foreach entry.
          */
-         expand_acc_source(opts, acc);
 -        if(expand_acc_source(acc) == 0)
++        if(expand_acc_source(opts, acc) == 0)
+         {
+             acc = acc->next;
+             continue;
+         }
  
          /* Now expand the open_ports string.
          */
@@@ -933,48 -910,8 +980,50 @@@ parse_access_file(fko_srv_options_t *op
                  clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
              }
              add_acc_string(&(curr_acc->key), val);
 +            curr_acc->key_len = strlen(curr_acc->key);
+             add_acc_bool(&(curr_acc->use_rijndael), "Y");
          }
 +        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);
 +            add_acc_b64_string(&(curr_acc->key),
 +                &(curr_acc->key_len), curr_acc->key_base64);
++            add_acc_bool(&(curr_acc->use_rijndael), "Y");
 +        }
 +        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);
 +            add_acc_b64_string(&(curr_acc->hmac_key),
 +                &curr_acc->hmac_key_len, curr_acc->hmac_key_base64);
 +        }
          else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT"))
          {
              add_acc_int(&(curr_acc->fw_access_timeout), val);
@@@ -273,11 -273,7 +273,12 @@@ typedef struct acc_stanz
      char                *restrict_ports;
      acc_port_list_t     *rport_list;
      char                *key;
 +    int                 key_len;
 +    char                *key_base64;
 +    char                *hmac_key;
 +    int                 hmac_key_len;
 +    char                *hmac_key_base64;
+     unsigned char       use_rijndael;
      int                 fw_access_timeout;
      unsigned char       enable_cmd_exec;
      char                *cmd_exec_user;
@@@ -363,9 -362,11 +363,9 @@@ incoming_spa(fko_srv_options_t *opts
          */
          enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
  
-         if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
+         if(acc->use_rijndael && enc_type == FKO_ENCRYPTION_RIJNDAEL)
          {
 -            if(acc->key != NULL)
 -                res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
 -            else
 +            if (acc->key == NULL)
              {
                  log_msg(LOG_ERR,
                      "(stanza #%d) No KEY for RIJNDAEL encrypted messages",
                  acc = acc->next;
                  continue;
              }
 +
 +            res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data,
 +                acc->key, acc->key_len, acc->encryption_mode, acc->hmac_key,
 +                acc->hmac_key_len);
          }
-         else if(enc_type == FKO_ENCRYPTION_GPG)
+         else if(acc->use_gpg && enc_type == FKO_ENCRYPTION_GPG)
          {
              /* For GPG we create the new context without decrypting on the fly
 -             * so we can set some  GPG parameters first.
 +             * so we can set some GPG parameters first.
              */
-             if(acc->gpg_decrypt_pw != NULL)
+             if(acc->gpg_decrypt_pw != NULL || acc->gpg_allow_no_pw)
              {
 -                res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
 +                res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL,
 +                        0, acc->encryption_mode, NULL, 0);
                  if(res != FKO_SUCCESS)
                  {
                      log_msg(LOG_WARNING,
  
                  /* Now decrypt the data.
                  */
 -                res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw);
 +                res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw, 0);
  
              }
-             else
-             {
-                 log_msg(LOG_ERR,
-                     "(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages, set GPG_ALLOW_NO_PW",
-                     stanza_num
-                 );
-                 acc = acc->next;
-                 continue;
-             }
          }
          else
          {
diff --cc server/utils.c
Simple merge
diff --cc server/utils.h
  */
  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);
+ int set_file_perms(const char *file);
+ int verify_file_perms_ownership(const char *file);
  
  size_t strlcat(char *dst, const char *src, size_t siz);
  size_t strlcpy(char *dst, const char *src, size_t siz);
@@@ -22,45 -21,34 +22,46 @@@ my $gpg_client_home_dir = "$conf_dir/cl
  my $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw";
  
  my %cf = (
 -    'nat'                  => "$conf_dir/nat_fwknopd.conf",
 -    'def'                  => "$conf_dir/default_fwknopd.conf",
 -    'def_access'           => "$conf_dir/default_access.conf",
 -    'exp_access'           => "$conf_dir/expired_stanza_access.conf",
 -    'future_exp_access'    => "$conf_dir/future_expired_stanza_access.conf",
 -    'exp_epoch_access'     => "$conf_dir/expired_epoch_stanza_access.conf",
 -    'invalid_exp_access'   => "$conf_dir/invalid_expire_access.conf",
 -    'force_nat_access'     => "$conf_dir/force_nat_access.conf",
 -    'local_nat'            => "$conf_dir/local_nat_fwknopd.conf",
 -    'ipfw_active_expire'   => "$conf_dir/ipfw_active_expire_equal_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",
 -    'broken_keys_access'   => "$conf_dir/multi_stanzas_with_broken_keys.conf",
 -    'open_ports_mismatch'  => "$conf_dir/mismatch_open_ports_access.conf",
 -    'require_user_access'  => "$conf_dir/require_user_access.conf",
 -    'user_mismatch_access' => "$conf_dir/mismatch_user_access.conf",
 -    'require_src_access'   => "$conf_dir/require_src_access.conf",
 -    'no_src_match'         => "$conf_dir/no_source_match_access.conf",
 -    'no_subnet_match'      => "$conf_dir/no_subnet_source_match_access.conf",
 -    'no_multi_src'         => "$conf_dir/no_multi_source_match_access.conf",
 -    'multi_src_access'     => "$conf_dir/multi_source_match_access.conf",
 -    'ip_src_match'         => "$conf_dir/ip_source_match_access.conf",
 -    'subnet_src_match'     => "$conf_dir/ip_source_match_access.conf",
 -    'disable_aging'        => "$conf_dir/disable_aging_fwknopd.conf",
 +    'nat'                     => "$conf_dir/nat_fwknopd.conf",
 +    'def'                     => "$conf_dir/default_fwknopd.conf",
 +    'def_access'              => "$conf_dir/default_access.conf",
 +    'hmac_access'             => "$conf_dir/hmac_access.conf",
 +    'exp_access'              => "$conf_dir/expired_stanza_access.conf",
 +    'future_exp_access'       => "$conf_dir/future_expired_stanza_access.conf",
 +    'exp_epoch_access'        => "$conf_dir/expired_epoch_stanza_access.conf",
 +    'invalid_exp_access'      => "$conf_dir/invalid_expire_access.conf",
 +    'force_nat_access'        => "$conf_dir/force_nat_access.conf",
 +    'local_nat'               => "$conf_dir/local_nat_fwknopd.conf",
 +    'ipfw_active_expire'      => "$conf_dir/ipfw_active_expire_equal_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",
 +    'broken_keys_access'      => "$conf_dir/multi_stanzas_with_broken_keys.conf",
 +    'ecb_mode_access'         => "$conf_dir/ecb_mode_access.conf",
 +    'ctr_mode_access'         => "$conf_dir/ctr_mode_access.conf",
 +    'cfb_mode_access'         => "$conf_dir/cfb_mode_access.conf",
 +    'ofb_mode_access'         => "$conf_dir/ofb_mode_access.conf",
 +    'open_ports_mismatch'     => "$conf_dir/mismatch_open_ports_access.conf",
 +    'require_user_access'     => "$conf_dir/require_user_access.conf",
 +    'user_mismatch_access'    => "$conf_dir/mismatch_user_access.conf",
 +    'require_src_access'      => "$conf_dir/require_src_access.conf",
 +    'invalid_src_access'      => "$conf_dir/invalid_source_access.conf",
 +    'no_src_match'            => "$conf_dir/no_source_match_access.conf",
 +    'no_subnet_match'         => "$conf_dir/no_subnet_source_match_access.conf",
 +    'no_multi_src'            => "$conf_dir/no_multi_source_match_access.conf",
 +    'multi_src_access'        => "$conf_dir/multi_source_match_access.conf",
 +    'ip_src_match'            => "$conf_dir/ip_source_match_access.conf",
 +    'subnet_src_match'        => "$conf_dir/ip_source_match_access.conf",
 +    'rc_file_def_key'         => "$conf_dir/fwknoprc_with_default_key",
 +    'rc_file_def_b64_key'     => "$conf_dir/fwknoprc_with_default_base64_key",
 +    'rc_file_named_key'       => "$conf_dir/fwknoprc_named_key",
 +    'rc_file_invalid_b64_key' => "$conf_dir/fwknoprc_invalid_base64_key",
 +    'rc_file_hmac_b64_key'    => "$conf_dir/fwknoprc_default_hmac_base64_key",
 +    'base64_key_access'       => "$conf_dir/base64_key_access.conf",
++    'disable_aging'           => "$conf_dir/disable_aging_fwknopd.conf",
  );
  
  my $default_digest_file = "$run_dir/digest.cache";
@@@ -1549,20 -1228,229 +1623,241 @@@ my @tests = 
          'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
          'fatal'    => $NO
      },
+     ### fuzzing tests
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'overly long port value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
+         # "tcp/`perl -e '{print "1"x"40"}'`" -a 127.0.0.2 -D 127.0.0.1 \
+         # --get-key local_spa.key --verbose --verbose
+         #
+         # This problem was found by Fernando Arnaboldi of IOActive and exploits
+         # a buffer overflow in the fwknopd servers prior to 2.0.3 from
+         # authenticated clients.
+         #
+         'fuzzing_pkt' =>
+             '+JzxeTGlc6lwwzbJSrYChKx8bonWBIPajwGfEtGOaoglcMLbTY/GGXo/nxqiN1LykFS' .
+             'lDFXgrkyx2emJ7NGzYqQPUYZxLdZRocR9aRIptvXLLIPBcIpJASi/TUiJlw7CDFMcj0' .
+             'ptSBJJUZi0tozpKHETp3AgqfzyOy5FNs38aZsV5/sDl3Pt+kF7fTZJ+YLbmYY4yCUz2' .
+             'ZUYoCaJ7X78ULyJTi5eT7nug',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'overly long proto value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
+         # "tcp`perl -e '{print "A"x"28"}'`/1" -a 127.0.0.2 -D 127.0.0.1 \
+         # --get-key local_spa.key --verbose --verbose
+         #
+         # This problem was found by Fernando Arnaboldi of IOActive and exploits
+         # a buffer overflow in the fwknopd servers prior to 2.0.3 from
+         # authenticated clients.
+         #
+         'fuzzing_pkt' =>
+             '/im5MiJQmOdzqrdWXv+AjEtAm/HsLrdaTFcSw3ZskqpGOdDIrSCz3VXbFfv7qDkc5Y4' .
+             'q/k1mRXl9SGzpug87U5dZSyCdAr30z7/2kUFEPTGOQBi/x+L1t1pvdkm4xg13t09ldm' .
+             '5OD8KiV6qzqLOvN4ULJjvvJJWBZ9qvo/f2Q9Wf67g2KHiwS6EeCINAuMoUw/mNRQMa4' .
+             'oGnOXu3/DeWHJAwtSeh7EAr4',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'overly long IP value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
+         # -a `perl -e '{print "1"x"136"}'`.0.0.1 -D 127.0.0.1 \
+         # --get-key local_spa.key --verbose --verbose
+         #
+         # This problem was found by Fernando Arnaboldi of IOActive and exploits
+         # a condition in which pre-2.0.3 fwknopd servers fail to properly validate
+         # allow IP addresses from malicious authenticated clients.
+         #
+         'fuzzing_pkt' =>
+             '93f2rhsXLmBoPicWvYTqrbp+6lNqvWDc8dzmX2s3settwjBGRAXm33TB9agibEphrBu' .
+             '3d+7DEsivZLDS6Kz0JwdjX7t0J9c8es+DVNjlLnPtVNcxhs+2kUzimNrgysIXQRJ+GF' .
+             'GbhdxiXCqdy1vWxWpdoaZmY/CeGIkpoFJFPbJhCRLLX25UMvMF2wXj02MpI4d3t1/6W' .
+             'DM3taM3kZsiFv6HxFjAhIEuQ1oAg2OgRGXkDmT3jDNZMHUm0d4Ahm9LonG7RbOxq/B0' .
+             'qUvY8lkymbwvjelVok7Lvlc06cRhN4zm32D4V05g0vQS3PlX9C+mgph9DeAPVX+D8iZ' .
+             '8lGrxcPSfbCOW61k0MP+q1EhLZkc1qAm5g2+2cLNZcoBNEdh3yj8OTPZJyBVw',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'negative port value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A \
+         # tcp/-33 -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
+         # --verbose --verbose
+         #
+         'fuzzing_pkt' =>
+             '/weoc+pEuQknZo8ImWTQBB+/PwSJ2/TcrmFoSkxpRXX4+jlUxoJakHrioxh8rhLmAD9' .
+             '8E4lMnq+EbM2XYdhs2alpZ5bovAFojMsYRWwr/BvRO4Um4Fmo9z9sY3DR477TXNYXBR' .
+             'iGXWxSL4u+AWSSePK3qiiYoRQVw',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'null port value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/ \
+         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
+         # --verbose --verbose
+         #
+         'fuzzing_pkt' =>
+             '94nu7hvq6V/3A27GzjHwfPnPCQfs44ySlraIFYHOAqy5YqjkrBS67nH35tX55N1BrYZ' .
+             '07zvcT03keUhLE1Uo7Wme1nE7BfTOG5stmIK1UQI85sL52//lDHu+xCqNcL7GUKbVRz' .
+             'ekw+EUscVvUkrsRcVtSvOm+fCNo',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'long FKO protocol value (enc mode trigger)',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
+         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
+         #
+         # This problem was found by Fernando Arnaboldi of IOActive and is designed
+         # to have fwknopd look for a mode decryption mode for a long Rijndael-
+         # encrypted SPA packet
+         #
+         'fuzzing_pkt' =>
+             '/ewH/k1XsDX+VQ8NlNvCZ4P2QOl/4IpJYXkq4TtAe3899OtApXJiTtPCuYW70XPuxge' .
+             'MtFjc4UfslK/r9v+FYfyd3fIIHCz0Q0M4+nM3agTLmJj8nOxk6ZeBj82SDQWhHAxGdJ' .
+             'IQALPve0ug4cuGxS3b4M+2Q/Av9i2tU3Lzlogw3sY0tk6wGf4zZk4UsviVXYpINniGT' .
+             'RhYSIQ1dfdkng7hKiHMDaObYY1GFp4nxEt/QjasAwvE+7/iFyoKN+IRpGG4v4hGEPh2' .
+             'vTDqmvfRuIHtgFD7NxZjt+m/jjcu0gkdWEoD4fenwGU35FlvchyM2AiAEw7yRzSABfn' .
+             'R9d3sYZGMtyASw2O1vSluwIxUUnDop3gxEIhJEj8h+01pA3K+klSpALeY9EZgHqYC7E' .
+             'ETuPS6dZ3764nWohtCY67JvNUX7TtNDNc2qrhrapdRP17+PT2Vh4s9m38V3WwVWC3uH' .
+             'X/klLZcHIt+aRDV+uekw9GOKSgwFL2ekPpr3gXxigc3zrxel5hcsqLOpVUa4CP/0HkG' .
+             'F0NPQvOT3ZvpeIJnirKP1ZX9gDFinqhuzL7oqktW61e1iwe7KZEdrZV0k2KZwyb8qU5' .
+             'rPAEnw',
+         'server_positive_output_matches' => [qr/No\sstanza\sencryption\smode\smatch/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'long FKO protocol value (Rijndael trigger)',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A tcp/22 \
+         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
+         #
+         # This problem was found by Fernando Arnaboldi of IOActive and is designed
+         # to have fwknopd look for a mode decryption mode for a long Rijndael-
+         # encrypted SPA packet
+         #
+         'fuzzing_pkt' =>
+             '+YQNu4BFgiNeu8HeiBiNKriqCFSseALt9vJaKzkzK/OF4pjkJcvhGEOi7fEVXqn3VIdlGR' .
+             'DmBul2I7H3z18U9E97bWGgT9NexKgEPCuekL18ZEPf5xR3JleNsNWatqYgAOkgN8ZWE69Q' .
+             'qQUYYhxTvJHS6R+5JqFKB3A44hMXoICdYNkn9MAktHxk3PbbpQ+nA+jESwVCra2doAiLiM' .
+             'ucvGIZZiTv0Mc1blFYIE2zqZ/C7ct1V+ukwSkUv0r87eA7uJhmlpThRsL0dN6iekJ6i87B' .
+             'tE8QyuOXzOMftI11SUn/LwqD4RMdR21rvLrzR6ZB5eUX2UBpODyzX6n+PJJkTWCuFVT4z1' .
+             'MKY',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
+     {
+         'category' => 'Rijndael SPA',
+         'subcategory' => 'FUZZING',
+         'detail'   => 'null proto value',
+         'err_msg'  => 'server crashed or did not detect error condition',
+         'function' => \&fuzzer,
+         ### this packet was generated with a modified fwknop client via the
+         ### following command line:
+         #
+         # LD_LIBRARY_PATH=../lib/.libs  ../client/.libs/fwknop -A /22 \
+         # -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
+         # --verbose --verbose
+         #
+         'fuzzing_pkt' =>
+             '/JT14qxh9P4iy+CuUZahThaQjoEuL2zd46a+jL6sTrBZJSa6faUX4dH5fte/4ZJv+9f' .
+             'd/diWYKAUvdQ4DydPGlR7mwQa2W+obKpqrsTBz7D4054z6ATAOGpCtifakEVl1XRc2+' .
+             'hW04WpY8mdUNu9i+PrfPr7/KxqU',
+         'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
+         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+             "$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
+             "-d $default_digest_file -p $default_pid_file $intf_str",
+         'fatal'    => $NO
+     },
      {
          'category' => 'Rijndael SPA',
 +        'subcategory' => 'client+server',
 +        'detail'   => 'replay detection (Rijndael prefix)',
 +        'err_msg'  => 'could not detect replay attack',
 +        'function' => \&replay_detection,
 +        'pkt_prefix' => 'U2FsdGVkX1',
 +        'cmdline'  => $default_client_args,
 +        'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
 +            "$fwknopdCmd $default_server_conf_args $intf_str",
 +        'fatal'    => $NO
 +    },
 +    {
 +        'category' => 'Rijndael SPA',
          'subcategory' => 'server',
          'detail'   => 'digest cache structure',
          'err_msg'  => 'improper digest cache structure',
@@@ -2636,6 -2463,44 +2987,44 @@@ sub altered_non_base64_spa_data() 
      return $rv;
  }
  
+ sub fuzzer() {
+     my $test_hr = shift;
+     my $rv = 1;
+     my $server_was_stopped = 0;
+     my $fw_rule_created = 0;
+     my $fw_rule_removed = 0;
+     my @packets = (
+         {
+             'proto'  => 'udp',
+             'port'   => $default_spa_port,
+             'dst_ip' => $loopback_ip,
+             'data'   => $test_hr->{'fuzzing_pkt'},
+         },
+     );
+     ($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
+         = &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
+     $rv = 0 unless $server_was_stopped;
+     if ($fw_rule_created) {
 -        &write_test_file("[-] new fw rule created.\n", $current_test_file);
++        &write_test_file("[-] new fw rule created.\n", $curr_test_file);
+         $rv = 0;
+     } else {
 -        &write_test_file("[+] new fw rule not created.\n", $current_test_file);
++        &write_test_file("[+] new fw rule not created.\n", $curr_test_file);
+     }
+     if ($test_hr->{'server_positive_output_matches'}) {
+         $rv = 0 unless &file_find_regex(
+             $test_hr->{'server_positive_output_matches'},
+             $MATCH_ALL, $server_test_file);
+     }
+     return $rv;
+ }
  sub altered_base64_spa_data() {
      my $test_hr = shift;
  
@@@ -3491,17 -3230,24 +3900,24 @@@ sub init() 
              copy $logfile, "${output_dir}.last/$logfile" or die $!;
          }
          $saved_last_results = 1;
+     } else {
+         mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
+     }
+     if (-d $run_dir) {
+         rmtree $run_dir or die $!;
      }
+     mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
  
 -    for my $file (glob("$output_dir/*.test")) {
 -        unlink $file or die "[*] Could not unlink($file)";
 -    }
 -    if (-e "$output_dir/init") {
 -        unlink "$output_dir/init" or die $!;
 +    for my $dir ($output_dir, $run_dir) {
 +        next if -d $dir;
 +        mkdir $dir or die "[*] Could not mkdir $dir: $!";
      }
  
 -    if (-e $logfile) {
 -        unlink $logfile or die $!;
 +    for my $file (glob("$output_dir/*.test"), "$output_dir/init",
 +            $tmp_rc_file, $logfile, $key_gen_file) {
 +        next unless -e $file;
 +        unlink $file or die "[*] Could not unlink($file)";
      }
  
      if ($test_include) {