Added FORCE_NAT mode to the access.conf file
authorMichael Rash <mbr@cipherdyne.org>
Thu, 1 Dec 2011 01:51:19 +0000 (20:51 -0500)
committerMichael Rash <mbr@cipherdyne.org>
Thu, 1 Dec 2011 01:51:19 +0000 (20:51 -0500)
This commit adds a new configuration variable "FORCE_NAT" to the access.conf
file:

    For any valid SPA packet, force the requested connection to be NAT'd
    through to the specified (usually internal) IP and port value.  This is
    useful if there are multiple internal systems running a service such as
    SSHD, and you want to give transparent access to only one internal system
    for each stanza in the access.conf file.  This way, multiple external
    users can each directly access only one internal system per SPA key.

This commit also implements a few minor code cleanups.

21 files changed:
client/config_init.c
client/fwknop_common.h
client/http_resolve_host.c
common/common.h
doc/fwknop.man.asciidoc
doc/fwknopd.man.asciidoc
server/access.c
server/fw_util.h
server/fw_util_ipf.c
server/fw_util_ipfw.c
server/fw_util_iptables.c
server/fw_util_pf.c
server/fwknopd.c
server/fwknopd_common.h
server/incoming_spa.c
server/tcp_server.c
test/conf/expired_stanza_access.conf
test/conf/force_nat_access.conf [new file with mode: 0644]
test/conf/future_expired_stanza_access.conf [new file with mode: 0644]
test/conf/invalid_expire_access.conf [new file with mode: 0644]
test/test-fwknop.pl

index 7a5ab72..f0cd41b 100644 (file)
@@ -240,7 +240,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val)
     else if(CONF_VAR_IS(var, "SPA_SERVER_PORT"))
     {
         tmpint = atoi(val);
-        if(tmpint < 0 || tmpint > 65535)
+        if(tmpint < 0 || tmpint > MAX_PORT)
             return(-1);
         else
             options->spa_dst_port = tmpint;
@@ -249,7 +249,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val)
     else if(CONF_VAR_IS(var, "SPA_SOURCE_PORT"))
     {
         tmpint = atoi(val);
-        if(tmpint < 0 || tmpint > 65535)
+        if(tmpint < 0 || tmpint > MAX_PORT)
             return(-1);
         else
             options->spa_src_port = tmpint;
@@ -277,7 +277,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val)
         else if(strcasecmp(val, "resolve") == 0)
             options->resolve_ip_http = 1;
         else /* Assume IP address */
-            strlcpy(options->allow_ip_str, val, MAX_IP_STR_LEN);
+            strlcpy(options->allow_ip_str, val, MAX_IPV4_STR_LEN);
     }
     /* Time Offset */
     else if(CONF_VAR_IS(var, "TIME_OFFSET"))
@@ -319,7 +319,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val)
     /* Spoof Source IP */
     else if(CONF_VAR_IS(var, "SPOOF_SOURCE_IP"))
     {
-        strlcpy(options->spoof_ip_src_str, val, MAX_IP_STR_LEN);
+        strlcpy(options->spoof_ip_src_str, val, MAX_IPV4_STR_LEN);
     }
     /* ACCESS request */
     else if(CONF_VAR_IS(var, "ACCESS"))
@@ -382,7 +382,7 @@ parse_rc_param(fko_cli_options_t *options, const char *var, char * val)
     else if(CONF_VAR_IS(var, "NAT_PORT"))
     {
         tmpint = atoi(val);
-        if(tmpint < 0 || tmpint > 65535)
+        if(tmpint < 0 || tmpint > MAX_PORT)
             return(-1);
         else
             options->nat_port = tmpint;
@@ -673,13 +673,13 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
     /* Reset the options index so we can run through them again.
     */
     optind = 0;
+
     while ((cmd_arg = getopt_long(argc, argv,
             GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) {
 
         switch(cmd_arg) {
             case 'a':
-                strlcpy(options->allow_ip_str, optarg, MAX_IP_STR_LEN);
+                strlcpy(options->allow_ip_str, optarg, MAX_IPV4_STR_LEN);
                 break;
             case 'A':
                 strlcpy(options->access_str, optarg, MAX_LINE_LEN);
@@ -740,7 +740,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
                 break;
             case 'p':
                 options->spa_dst_port = atoi(optarg);
-                if (options->spa_dst_port < 0 || options->spa_dst_port > 65535)
+                if (options->spa_dst_port < 0 || options->spa_dst_port > MAX_PORT)
                 {
                     fprintf(stderr, "Unrecognized port: %s\n", optarg);
                     exit(EXIT_FAILURE);
@@ -754,7 +754,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
                 }
                 break;
             case 'Q':
-                strlcpy(options->spoof_ip_src_str, optarg, MAX_IP_STR_LEN);
+                strlcpy(options->spoof_ip_src_str, optarg, MAX_IPV4_STR_LEN);
                 break;
             case 'r':
                 options->rand_port = 1;
@@ -777,11 +777,11 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
                 options->show_last_command = 1;
                 break;
             case 's':
-                strlcpy(options->allow_ip_str, "0.0.0.0", MAX_IP_STR_LEN);
+                strlcpy(options->allow_ip_str, "0.0.0.0", MAX_IPV4_STR_LEN);
                 break;
             case 'S':
                 options->spa_src_port = atoi(optarg);
-                if (options->spa_src_port < 0 || options->spa_src_port > 65535)
+                if (options->spa_src_port < 0 || options->spa_src_port > MAX_PORT)
                 {
                     fprintf(stderr, "Unrecognized port: %s\n", optarg);
                     exit(EXIT_FAILURE);
@@ -827,7 +827,7 @@ config_init(fko_cli_options_t *options, int argc, char **argv)
                 break;
             case NAT_PORT:
                 options->nat_port = atoi(optarg);
-                if (options->nat_port < 0 || options->nat_port > 65535)
+                if (options->nat_port < 0 || options->nat_port > MAX_PORT)
                 {
                     fprintf(stderr, "Unrecognized port: %s\n", optarg);
                     exit(EXIT_FAILURE);
index e0f3e30..5d7e8ef 100644 (file)
@@ -85,8 +85,8 @@ typedef struct fko_cli_options
     int  run_last_command;
     int  no_save_args;
     char spa_server_str[MAX_SERVER_STR_LEN];  /* may be a hostname */
-    char allow_ip_str[MAX_IP_STR_LEN];
-    char spoof_ip_src_str[MAX_IP_STR_LEN];
+    char allow_ip_str[MAX_IPV4_STR_LEN];
+    char spoof_ip_src_str[MAX_IPV4_STR_LEN];
     char spoof_user[MAX_USERNAME_LEN];
     int  rand_port;
     char gpg_recipient_key[MAX_GPG_KEY_ID];
index f6afe61..9282437 100644 (file)
@@ -78,7 +78,7 @@ parse_url(char *res_url, struct url* url)
     if(e_ndx != NULL)
     {
         port = atoi(e_ndx+1);
-        if(port < 1 || port > 65535)
+        if(port < 1 || port > MAX_PORT)
         {
             fprintf(stderr, "resolve-url port value is invalid.\n");
             return(-1);
@@ -251,7 +251,7 @@ resolve_ip_http(fko_cli_options_t *options)
         * Note: We are expecting the content to be just an IP address
         *       (possibly followed by whitespace or other not-digit value).
        */
-       for(i=0; i<MAX_IP_STR_LEN; i++) {
+       for(i=0; i<MAX_IPV4_STR_LEN; i++) {
                if(! isdigit(*(ndx+i)) && *(ndx+i) != '.')
                        break;
        }
@@ -269,7 +269,7 @@ resolve_ip_http(fko_cli_options_t *options)
                && o3 >= 0 && o3 <= 255
                && o4 >= 0 && o4 <= 255)
        {
-               strlcpy(options->allow_ip_str, ndx, MAX_IP_STR_LEN);
+               strlcpy(options->allow_ip_str, ndx, MAX_IPV4_STR_LEN);
 
                if(options->verbose)
                        printf("Resolved external IP (via http://%s%s) as: %s\n",
index 80d9277..c063cfd 100644 (file)
@@ -104,8 +104,8 @@ enum {
 #define MAX_PORT            65535
 #define MAX_PORT_STR_LEN    6
 #define MAX_PROTO_STR_LEN   6
-#define MAX_IP_STR_LEN      16
-#define MIN_IP_STR_LEN      9
+#define MAX_IPV4_STR_LEN    16
+#define MIN_IPV4_STR_LEN    9
 #define MAX_SERVER_STR_LEN  50
 
 #define MAX_LINE_LEN        1024
index 57d8395..f110d20 100644 (file)
@@ -162,7 +162,7 @@ SPA OPTIONS
     Specify IP address that should be permitted through the destination
     *fwknopd* server firewall (this IP is encrypted within the SPA packet
     itself).  This is useful to prevent a MTIM attack where a SPA packet
-    can be intercepted enroute and sent from a different IP than the
+    can be intercepted en-route and sent from a different IP than the
     original.  Hence, if the *fwknopd* server trusts the source address
     on the  SPA  packet IP header then the attacker gains access. 
     The *-a* option puts the source address within the encrypted SPA
@@ -359,7 +359,7 @@ commented out.  It is up to the user to edit this file to meet their needs.
 
 The '.fwknoprc' file contains a default configuration area or stanza which
 holds global configuration directives that override the program defaults. 
-You can edit this file and create additonal 'named stanzas' that can be
+You can edit this file and create additional 'named stanzas' that can be
 specified with the *-n* or *--named-config* option. Parameters defined in
 the named stanzas will override any matching 'default' stanza directives.
 Note that command-line options will still override any corresponding
index f0e4a20..a4b8666 100644 (file)
@@ -29,7 +29,7 @@ vulnerabilities (both 0-day and unpatched code) much more difficult.
 The main configuration for *fwknopd* is maintained within two files:
 'fwknopd.conf' and 'access.conf'.  The default location for these files
 is determined at package configuration (typically '@sysconfdir@/fwknop')The
-configuration variables within these files are desribed below.
+configuration variables within these files are described below.
 
 
 COMMAND-LINE OPTIONS
@@ -183,7 +183,7 @@ See the 'fwknopd.conf' file for the full list and corresponding details.
 *ACCESS_EXPIRE_EPOCH* '<seconds>'::
     Defines an expiration date for the access stanza as the epoch time, and is
     useful if a more accurate expiration time needs to be given than the day
-    resolution offered by the ACCESS_EXPIRE variable agove.  All SPA packets
+    resolution offered by the ACCESS_EXPIRE variable above.  All SPA packets
     that match an expired stanza will be ignored.  This parameter is optional.
 
 *ENABLE_DIGEST_PERSISTENCE* '<Y/N>'::
@@ -221,7 +221,7 @@ See the 'fwknopd.conf' file for the full list and corresponding details.
     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 'fwknopd.conf' file), but if you want
-    *fwknopd* to use the SNAT target, you mus also define an IP address with
+    *fwknopd* to use the SNAT target, you must also define an IP address with
     the ``SNAT_TRANSLATE_IP'' variable.
 
 *ENABLE_IPT_OUTPUT* '<Y/N>'::
@@ -306,7 +306,7 @@ is granted to *fwknop* clients that have generated the appropriate
 encrypted message.
 
 The 'access.conf' variables described below provide the access directives
-for the SPA packets with a source (or embeded request) IP that matches an
+for the SPA packets with a source (or embedded request) IP that matches an
 address or network range defined by the ``SOURCE'' variable.  All variables
 following ``SOURCE'' apply to the source 'stanza'.  Each ``SOURCE''
 directive starts a new stanza.
@@ -368,6 +368,14 @@ directive starts a new stanza.
     to be used to automatically resolve the external address (if the
     client behind a NAT) or the client must know the external IP.
 
+*FORCE_NAT*: '<IP> <PORT>'::
+    For any valid SPA packet, force the requested connection to be NAT'd
+    through to the specified (usually internal) IP and port value.  This is
+    useful if there are multiple internal systems running a service such as
+    SSHD, and you want to give transparent access to only one internal system
+    for each stanza in the access.conf file.  This way, multiple external
+    users can each directly access only one internal system per SPA key.
+
 *GPG_HOME_DIR*: '<path>'::
     Define the path to the GnuPG directory to be used by the *fwknopd*
     server.  If this keyword is not specified within 'access.conf' then
@@ -442,7 +450,7 @@ are received.
 
 SEE ALSO
 --------
-fwknop(8), iptables(8), libfko docmentation.
+fwknop(8), iptables(8), libfko documentation.
 
 
 AUTHOR
index d7ebcc3..3633e88 100644 (file)
@@ -80,11 +80,12 @@ add_acc_expire_time(fko_srv_options_t *opts, time_t *access_expire_time, const c
 
     memset(&tm, 0, sizeof(struct tm));
 
-    if (sscanf(val, "%d/%d/%d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
+    if (sscanf(val, "%2d/%2d/%4d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
     {
 
         log_msg(LOG_ERR,
-            "Fatal invalid epoch seconds value for access stanza expiration time"
+            "Fatal: invalid date value '%s' (need MM/DD/YYYY) for access stanza expiration time",
+            val
         );
         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
     }
@@ -120,7 +121,8 @@ add_acc_expire_time_epoch(fko_srv_options_t *opts, time_t *access_expire_time, c
     if (errno == ERANGE || (errno != 0 && expire_time == 0))
     {
         log_msg(LOG_ERR,
-            "Fatal invalid epoch seconds value for access stanza expiration time"
+            "Fatal: invalid epoch seconds value '%s' for access stanza expiration time",
+            val
         );
         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
     }
@@ -130,6 +132,34 @@ add_acc_expire_time_epoch(fko_srv_options_t *opts, time_t *access_expire_time, c
     return;
 }
 
+static void
+add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
+{
+    char      ip_str[MAX_IPV4_STR_LEN] = {0};
+
+    if (sscanf(val, "%15s %5u", ip_str, &curr_acc->force_nat_port) != 2)
+    {
+
+        log_msg(LOG_ERR,
+            "Fatal: invalid FORCE_NAT arg '%s', need <IP> <PORT>",
+            val
+        );
+        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
+    }
+
+    if (curr_acc->force_nat_port > MAX_PORT)
+    {
+        log_msg(LOG_ERR,
+            "Fatal: invalid FORCE_NAT port '%d'", curr_acc->force_nat_port);
+        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
+    }
+
+    curr_acc->force_nat = 1;
+    add_acc_string(&(curr_acc->force_nat_ip), ip_str);
+
+    return;
+}
+
 /* Take an IP or Subnet/Mask and convert it to mask for later
  * comparisons of incoming source IPs against this mask.
 */
@@ -137,7 +167,7 @@ static void
 add_source_mask(acc_stanza_t *acc, const char *ip)
 {
     char                *ndx;
-    char                ip_str[16] = {0};
+    char                ip_str[MAX_IPV4_STR_LEN] = {0};
     uint32_t            mask;
 
     struct in_addr      in;
@@ -523,6 +553,9 @@ free_acc_stanza_data(acc_stanza_t *acc)
         free_acc_port_list(acc->rport_list);
     }
 
+    if(acc->force_nat_ip != NULL)
+        free(acc->force_nat_ip);
+
     if(acc->key != NULL)
         free(acc->key);
 
@@ -915,6 +948,16 @@ parse_access_file(fko_srv_options_t *opts)
         {
             add_acc_expire_time_epoch(opts, &(curr_acc->access_expire_time), val);
         }
+        else if(CONF_VAR_IS(var, "FORCE_NAT"))
+        {
+            if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 )
+            {
+                fprintf(stderr,
+                    "[*] FORCE_NAT requires ENABLE_IPT_FORWARDING to be enabled in fwknopd.conf\n");
+                clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
+            }
+            add_acc_force_nat(opts, curr_acc, val);
+        }
         else
         {
             fprintf(stderr,
index b5afc1b..1f520fe 100644 (file)
@@ -63,7 +63,7 @@ void fw_initialize(const fko_srv_options_t *opts);
 int fw_cleanup(const fko_srv_options_t *opts);
 void check_firewall_rules(const fko_srv_options_t *opts);
 int fw_dump_rules(const fko_srv_options_t *opts);
-int process_spa_request(const fko_srv_options_t *opts, spa_data_t *spdat);
+int process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spdat);
 
 #endif /* FW_UTIL_H */
 
index 7233749..37937aa 100644 (file)
@@ -109,11 +109,11 @@ fw_cleanup(void)
 /* Rule Processing - Create an access request...
 */
 int
-process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
+process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spadat)
 {
     /* TODO: Implement me */
 
-    char             nat_ip[16] = {0};
+    char             nat_ip[MAX_IPV4_STR_LEN] = {0};
     char            *ndx;
 
     unsigned int     nat_port = 0;;
index f82def0..6e2370e 100644 (file)
@@ -415,7 +415,7 @@ fw_cleanup(const fko_srv_options_t *opts)
 /* Rule Processing - Create an access request...
 */
 int
-process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
+process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spadat)
 {
     unsigned short   rule_num;
 
index e6ccd84..a1a36ec 100644 (file)
@@ -479,9 +479,9 @@ fw_cleanup(const fko_srv_options_t *opts)
 /* Rule Processing - Create an access request...
 */
 int
-process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
+process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spadat)
 {
-    char             nat_ip[16] = {0};
+    char             nat_ip[MAX_IPV4_STR_LEN] = {0};
     char             snat_target[SNAT_TARGET_BUFSIZE] = {0};
     char            *ndx;
 
@@ -525,8 +525,8 @@ process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
     /* For straight access requests, we currently support multiple proto/port
      * request.
     */
-    if(spadat->message_type == FKO_ACCESS_MSG
-      || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG)
+    if((spadat->message_type == FKO_ACCESS_MSG
+      || spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG) && !acc->force_nat)
     {
 
         /* Check to make sure that the jump rules exist for each
@@ -578,7 +578,7 @@ process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
                     in_chain->next_expire = exp_ts;
             }
             else
-                log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); 
+                log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
 
             /* If we have to make an corresponding OUTPUT rule if out_chain target
             * is not NULL.
@@ -631,19 +631,25 @@ process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
     else if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
       || spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG
       || spadat->message_type == FKO_NAT_ACCESS_MSG
-      || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG  )
+      || spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
+      || acc->force_nat)
     {
         /* Parse out the NAT IP and Port components.
         */
-        ndx = strchr(spadat->nat_access, ',');
-        if(ndx != NULL)
+        if(acc->force_nat)
         {
-            strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1);
-            nat_port = atoi(ndx+1);
+            strlcpy(nat_ip, acc->force_nat_ip, MAX_IPV4_STR_LEN);
+            nat_port = acc->force_nat_port;
+        }
+        else
+        {
+            ndx = strchr(spadat->nat_access, ',');
+            if(ndx != NULL)
+            {
+                strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1);
+                nat_port = atoi(ndx+1);
+            }
         }
-
-// --DSS temp
-//fprintf(stderr, "NAT IP: '%s', NAT PORT: '%i'\n", nat_ip, nat_port);
 
         /* Make our FORWARD and NAT rules
         */
index 22e32d0..8f10071 100644 (file)
@@ -204,7 +204,7 @@ fw_cleanup(const fko_srv_options_t *opts)
 /* Rule Processing - Create an access request...
 */
 int
-process_spa_request(const fko_srv_options_t *opts, spa_data_t *spadat)
+process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_data_t *spadat)
 {
     char             new_rule[MAX_PF_NEW_RULE_LEN];
     char             write_cmd[CMD_BUFSIZE];
index 337ac0e..5a780b3 100644 (file)
@@ -282,7 +282,7 @@ main(int argc, char **argv)
         if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0)
         {
             if(atoi(opts.config[CONF_TCPSERV_PORT]) <= 0
-              || atoi(opts.config[CONF_TCPSERV_PORT]) >  65535)
+              || atoi(opts.config[CONF_TCPSERV_PORT]) > MAX_PORT)
             {
                 log_msg(LOG_WARNING,
                     "WARNING: ENABLE_TCP_SERVER is set, but TCPSERV_PORT is not valid. TCP server not started!"
index 817891e..4fe7ec4 100644 (file)
@@ -282,6 +282,10 @@ typedef struct acc_stanza
     acc_string_list_t   *gpg_remote_id_list;
     time_t              access_expire_time;
     int                 expired;
+    unsigned char       force_nat;
+    char                *force_nat_ip;
+    char                *force_nat_proto;
+    unsigned int        force_nat_port;
     struct acc_stanza   *next;
 } acc_stanza_t;
 
@@ -386,8 +390,8 @@ typedef struct spa_data
     char           *version;
     short           message_type;
     char           *spa_message;
-    char            spa_message_src_ip[MAX_IP_STR_LEN];
-    char            pkt_source_ip[MAX_IP_STR_LEN];
+    char            spa_message_src_ip[MAX_IPV4_STR_LEN];
+    char            pkt_source_ip[MAX_IPV4_STR_LEN];
     char            spa_message_remain[1024]; /* --DSS FIXME: arbitrary bounds */
     char           *nat_access;
     char           *server_auth;
index ac4e5f1..c625e4d 100644 (file)
@@ -616,7 +616,7 @@ incoming_spa(fko_srv_options_t *opts)
          * access stanza loop (first valid access stanza stops us looking
          * for others).
         */
-        process_spa_request(opts, &spadat);
+        process_spa_request(opts, acc, &spadat);
         if(ctx != NULL)
             fko_destroy(ctx);
         break;
index 1cab2bc..77c0ac3 100644 (file)
@@ -60,7 +60,7 @@ run_tcp_server(fko_srv_options_t *opts)
     fd_set              sfd_set;
     struct sockaddr_in  saddr, caddr;
     struct timeval      tv;
-    char                sipbuf[MAX_IP_STR_LEN];
+    char                sipbuf[MAX_IPV4_STR_LEN];
 
     unsigned short      port = atoi(opts->config[CONF_TCPSERV_PORT]);
 
@@ -196,8 +196,8 @@ run_tcp_server(fko_srv_options_t *opts)
 
         if(opts->verbose)
         {
-            memset(sipbuf, 0x0, MAX_IP_STR_LEN);
-            inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IP_STR_LEN);
+            memset(sipbuf, 0x0, MAX_IPV4_STR_LEN);
+            inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN);
             log_msg(LOG_INFO, "tcp_server: Got TCP connection from %s.", sipbuf);
         }
 
index a928005..8678550 100644 (file)
@@ -1,4 +1,4 @@
 SOURCE: ANY;
 KEY: fwknoptest;
 FW_ACCESS_TIMEOUT:  3;
-ACCESS_EXPIRE: 3/10/1999;   ### very old
+ACCESS_EXPIRE: 3/10/01;   ### very old
diff --git a/test/conf/force_nat_access.conf b/test/conf/force_nat_access.conf
new file mode 100644 (file)
index 0000000..b2f18b0
--- /dev/null
@@ -0,0 +1,4 @@
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT:  3;
+FORCE_NAT: 192.168.1.123 22;
diff --git a/test/conf/future_expired_stanza_access.conf b/test/conf/future_expired_stanza_access.conf
new file mode 100644 (file)
index 0000000..c39675b
--- /dev/null
@@ -0,0 +1,4 @@
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT:  3;
+ACCESS_EXPIRE: 3/10/2500;   ### way in the future
diff --git a/test/conf/invalid_expire_access.conf b/test/conf/invalid_expire_access.conf
new file mode 100644 (file)
index 0000000..2dd3d24
--- /dev/null
@@ -0,0 +1,4 @@
+SOURCE: ANY;
+KEY: fwknoptest;
+FW_ACCESS_TIMEOUT:  3;
+ACCESS_EXPIRE: 3-10-01;  ### / separators required
index 516b5e4..0be4cce 100755 (executable)
@@ -23,7 +23,10 @@ my $nat_conf            = "$conf_dir/nat_fwknopd.conf";
 my $default_conf        = "$conf_dir/default_fwknopd.conf";
 my $default_access_conf = "$conf_dir/default_access.conf";
 my $expired_access_conf = "$conf_dir/expired_stanza_access.conf";
+my $future_expired_access_conf = "$conf_dir/future_expired_stanza_access.conf";
 my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
+my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
+my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
 my $gpg_access_conf     = "$conf_dir/gpg_access.conf";
 my $default_digest_file = "$run_dir/digest.cache";
 my $default_pid_file    = "$run_dir/fwknopd.pid";
@@ -53,6 +56,7 @@ my $gpg_client_key = '6A3FAD56';
 my $loopback_ip = '127.0.0.1';
 my $fake_ip     = '127.0.0.2';
 my $internal_nat_host = '192.168.1.2';
+my $force_nat_host = '192.168.1.123';
 my $default_spa_port = 62201;
 my $non_std_spa_port = 12345;
 
@@ -629,6 +633,20 @@ my @tests = (
     {
         'category' => 'Rijndael SPA',
         'subcategory' => 'client+server',
+        'detail'   => 'invalid expire date (tcp/22 ssh)',
+        'err_msg'  => 'SPA packet accepted',
+        'function' => \&spa_cycle,
+        'cmdline'  => $default_client_args,
+        'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+            "$fwknopdCmd -c $default_conf -a $invalid_expire_access_conf " .
+            "-d $default_digest_file -p $default_pid_file $intf_str",
+        'server_positive_output_matches' => [qr/invalid\sdate\svalue/],
+        'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
+        'fatal'    => $NO
+    },
+    {
+        'category' => 'Rijndael SPA',
+        'subcategory' => 'client+server',
         'detail'   => 'expired epoch stanza (tcp/22 ssh)',
         'err_msg'  => 'SPA packet accepted',
         'function' => \&spa_cycle,
@@ -640,6 +658,20 @@ my @tests = (
         'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
         'fatal'    => $NO
     },
+    {
+        'category' => 'Rijndael SPA',
+        'subcategory' => 'client+server',
+        'detail'   => 'future expired stanza (tcp/22 ssh)',
+        'err_msg'  => 'SPA packet not accepted',
+        'function' => \&spa_cycle,
+        'cmdline'  => $default_client_args,
+        'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+            "$fwknopdCmd -c $default_conf -a $future_expired_access_conf " .
+            "-d $default_digest_file -p $default_pid_file $intf_str",
+        'fw_rule_created' => $NEW_RULE_REQUIRED,
+        'fw_rule_removed' => $NEW_RULE_REMOVED,
+        'fatal'    => $NO
+    },
 
     {
         'category' => 'Rijndael SPA',
@@ -866,6 +898,25 @@ my @tests = (
         'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
             "$fwknopdCmd -c $nat_conf -a $open_ports_access_conf " .
             "-d $default_digest_file -p $default_pid_file $intf_str",
+        'server_positive_output_matches' => [qr/to\:$internal_nat_host\:22/i],
+        'fw_rule_created' => $NEW_RULE_REQUIRED,
+        'fw_rule_removed' => $NEW_RULE_REMOVED,
+        'server_conf' => $nat_conf,
+        'fatal'    => $NO
+    },
+
+    {
+        'category' => 'Rijndael SPA',
+        'subcategory' => 'client+server',
+        'detail'   => "force NAT $force_nat_host (tcp/22 ssh)",
+        'err_msg'  => "could not complete NAT SPA cycle",
+        'function' => \&spa_cycle,
+        'cmdline'  => $default_client_args,
+        'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+            "$fwknopdCmd -c $nat_conf -a $force_nat_access_conf " .
+            "-d $default_digest_file -p $default_pid_file $intf_str",
+        'server_positive_output_matches' => [qr/to\:$force_nat_host\:22/i],
+        'server_negative_output_matches' => [qr/to\:$internal_nat_host\:22/i],
         'fw_rule_created' => $NEW_RULE_REQUIRED,
         'fw_rule_removed' => $NEW_RULE_REMOVED,
         'server_conf' => $nat_conf,
@@ -932,6 +983,22 @@ my @tests = (
         'fw_rule_removed' => $NEW_RULE_REMOVED,
         'fatal'    => $NO
     },
+
+    {
+        'category' => 'Rijndael SPA',
+        'subcategory' => 'client+server',
+        'detail'   => 'random SPA port (tcp/22 ssh)',
+        'err_msg'  => 'could not complete SPA cycle',
+        'function' => \&spa_cycle,
+        'cmdline'  => "$default_client_args -r",
+        'fwknopd_cmdline'  => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+            "$fwknopdCmd $default_server_conf_args $intf_str " .
+            qq|-P "udp"|,
+        'fw_rule_created' => $NEW_RULE_REQUIRED,
+        'fw_rule_removed' => $NEW_RULE_REMOVED,
+        'fatal'    => $NO
+    },
+
     {
         'category' => 'Rijndael SPA',
         'subcategory' => 'client+server',
@@ -2371,6 +2438,9 @@ sub init() {
             $multi_stanzas_access_conf,
             $expired_access_conf,
             $expired_epoch_access_conf,
+            $future_expired_access_conf,
+            $invalid_expire_access_conf,
+            $force_nat_access_conf,
     ) {
         die "[*] $file does not exist" unless -e $file;
     }