Exemple #1
0
/**
 * @brief Check a string to find out if it is built as 'hostname,port'
 *
 * This function check if we can extract an hostname and a port from the string.
 * If yes, we return 1, and both the hostname buffer and the port number are set
 * accordingly.
 *
 * We could have used sscanf() here with a template "%[^,],%u", but this way we
 * do not limit the size of the value copy in the hostname destination buffer.
 * Limiting the string in the sscanf() can be done but would prevent any easy change
 * for the hostname buffer size.
 *
 * @param str String to parse.
 * @param hostname Buffer where to store the hostname value read from @str.
 * @param hostname_bufsize Hostname buffer size.
 * @param port Value of the port read from @str.
 *
 * @return 1 if the string is built as 'hostname,port', 0 otherwise.
 */
static int
is_hostname_str_with_port(const char *str, char *hostname, size_t hostname_bufsize, int *port)
{
    int     valid = 0;                /* Result of the function */
    char    buf[MAX_LINE_LEN] = {0};  /* Copy of the buffer eg. "hostname,port" */
    char   *h;                        /* Pointer on the hostname string */
    char   *p;                        /* Ponter on the port string */

    memset(hostname, 0, hostname_bufsize);
    *port = 0;

    /* Replace the comma in the string with a NULL char to split the
     * buffer in two strings (hostname and port) */
    strlcpy(buf, str, sizeof(buf));
    p = strchr(buf, ',');

    if(p != NULL)
    {
        *p++ = 0;
        h = buf;

        *port = atoi(p);

        /* If the string does not match an ipv4 or ipv6 address we assume this
         * is an hostname. We make sure the port is in the good range too */
        if (   (is_valid_ipv4_addr(buf) == 0)
            && (is_ipv6_str(buf) == 0)
            && ((*port > 0) && (*port < 65536)) )
        {
            strlcpy(hostname, h, hostname_bufsize);
            valid = 1;
        }

        /* The port is out of range or the ip is an ipv6 or ipv4 address */
        else;
    }

    /* No port found in the string, let's skip */
    else;

    return valid;
}
Exemple #2
0
/* Process the SPA packet data
*/
void
incoming_spa(fko_srv_options_t *opts)
{
    /* Always a good idea to initialize ctx to null if it will be used
     * repeatedly (especially when using fko_new_with_data()).
    */
    fko_ctx_t       ctx = NULL;

    char            *spa_ip_demark, *raw_digest = NULL;
    int             res, enc_type, stanza_num=0;
    int             added_replay_digest = 0;
    int             is_err, cmd_exec_success = 0, attempted_decrypt = 0;
    int             conf_pkt_age = 0;
    char            dump_buf[CTX_DUMP_BUFSIZE];

    spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);

    /* This will hold our pertinent SPA data.
    */
    spa_data_t spadat;

    /* Loop through all access stanzas looking for a match
    */
    acc_stanza_t        *acc = opts->acc_stanzas;

    inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
        spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));

    inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip),
        spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip));

    /* At this point, we want to validate and (if needed) preprocess the
     * SPA data and/or to be reasonably sure we have a SPA packet (i.e
     * try to eliminate obvious non-spa packets).
    */
    if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest))
        return;

    if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
    {
        conf_pkt_age = strtol_wrapper(opts->config[CONF_MAX_SPA_PACKET_AGE],
                0, RCHK_MAX_SPA_PACKET_AGE, NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
        {
            log_msg(LOG_ERR, "[*] [%s] invalid MAX_SPA_PACKET_AGE", spadat.pkt_source_ip);
            return;
        }
    }

    /* Now that we know there is a matching access.conf stanza and the
     * incoming SPA packet is not a replay, see if we should grant any
     * access
    */
    while(acc)
    {
        res = FKO_SUCCESS;
        cmd_exec_success  = 0;
        attempted_decrypt = 0;
        stanza_num++;

        /* Start access loop with a clean FKO context
        */
        if(ctx != NULL)
        {
            if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                    spadat.pkt_source_ip, stanza_num
                );
            ctx = NULL;
        }

        /* Check for a match for the SPA source and destination IP and the access stanza
        */
        if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
            stanza_num, spadat.pkt_source_ip);

        log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data);

        /* Make sure this access stanza has not expired
        */
        if(! check_stanza_expiration(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Get encryption type and try its decoding routine first (if the key
         * for that type is set)
        */
        enc_type = fko_encryption_type((char *)spa_pkt->packet_data);

        if(! handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx,
                    &attempted_decrypt, &cmd_exec_success, enc_type,
                    stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt,
                    cmd_exec_success, enc_type, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt,
                    enc_type, stanza_num, res))
        {
            acc = acc->next;
            continue;
        }

        /* Add this SPA packet into the replay detection cache
        */
        if(! add_replay_cache(opts, acc, &spadat, raw_digest,
                    &added_replay_digest, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        /* At this point the SPA data is authenticated via the HMAC (if used
         * for now). Next we need to see if it meets our access criteria which
         * the server imposes regardless of the content of the SPA packet.
        */
        log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):",
            spadat.pkt_source_ip, stanza_num, res);

        res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf));
        if (res == FKO_SUCCESS)
            log_msg(LOG_DEBUG, "%s", dump_buf);
        else
            log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res));

        /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
         * then we need to make sure this incoming message is signer ID matches
         * an entry in the list.
        */

        if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        /* Populate our spa data struct for future reference.
        */
        res = get_spa_data_fields(ctx, &spadat);

        if(res != FKO_SUCCESS)
        {
            log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        /* Figure out what our timeout will be. If it is specified in the SPA
         * data, then use that.  If not, try the FW_ACCESS_TIMEOUT from the
         * access.conf file (if there is one).  Otherwise use the default.
        */
        set_timeout(acc, &spadat);

        /* Check packet age if so configured.
        */
        if(! check_pkt_age(opts, &spadat, stanza_num, conf_pkt_age))
        {
            acc = acc->next;
            continue;
        }

        /* At this point, we have enough to check the embedded (or packet source)
         * IP address against the defined access rights.  We start by splitting
         * the spa msg source IP from the remainder of the message.
        */
        spa_ip_demark = strchr(spadat.spa_message, ',');
        if(spa_ip_demark == NULL)
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1
                || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN)
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num);
            break;
        }

        strlcpy(spadat.spa_message_src_ip,
            spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);

        if(! is_valid_ipv4_addr(spadat.spa_message_src_ip))
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));
            break;
        }

        strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN);

        /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
         * is allowed.
        */
        if(! check_src_access(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
         * matches.
        */
        if(! check_username(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Take action based on SPA message type.
        */
        if(! check_nat_access_types(opts, acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Command messages.
        */
        if(spadat.message_type == FKO_COMMAND_MSG)
        {
            if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res))
            {
                /* we processed the command on a matching access stanza, so we
                 * don't look for anything else to do with this SPA packet
                */
                break;
            }
            else
            {
                acc = acc->next;
                continue;
            }
        }

        /* From this point forward, we have some kind of access message. So
         * we first see if access is allowed by checking access against
         * restrict_ports and open_ports.
         *
         *  --DSS TODO: We should add BLACKLIST support here as well.
        */
        if(! check_port_proto(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* At this point, we process the SPA request and break out of the
         * access stanza loop (first valid access stanza stops us looking
         * for others).
        */
        if(opts->test)  /* no firewall changes in --test mode */
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.",
                spadat.pkt_source_ip, stanza_num
            );
            acc = acc->next;
            continue;
        }
        else
        {
            process_spa_request(opts, acc, &spadat);
        }

        /* If we made it here, then the SPA packet was processed according
         * to a matching access.conf stanza, so we're done with this packet.
        */
        break;
    }

    if (raw_digest != NULL)
        free(raw_digest);

    if(ctx != NULL)
    {
        if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                spadat.pkt_source_ip, stanza_num
            );
        ctx = NULL;
    }

    return;
}
Exemple #3
0
/* Process the SPA packet data
*/
void
incoming_spa(fko_srv_options_t *opts)
{
    /* Always a good idea to initialize ctx to null if it will be used
     * repeatedly (especially when using fko_new_with_data()).
    */
    fko_ctx_t       ctx = NULL;

    char            *spa_ip_demark, *raw_digest = NULL;
    int             res, enc_type, stanza_num=0;
    int             added_replay_digest = 0;
    int             cmd_exec_success = 0, attempted_decrypt = 0;
    char            dump_buf[CTX_DUMP_BUFSIZE];

    spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);

    /* This will hold our pertinent SPA data.
    */
    spa_data_t spadat;

    /* Loop through all access stanzas looking for a match
    */
    //²éÕÒËùÓеÄstanzas£¬Æ¥ÅäÏàÓ¦Ïî¡£
    acc_stanza_t        *acc = opts->acc_stanzas;

    inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
        spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));	//»ñÈ¡Êý¾Ý±¨À´Ô´µØÖ·¡£

    inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip),
        spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip));	//»ñÈ¡Êý¾Ý±¨Ä¿µÄµØÖ·¡£

    /* At this point, we want to validate and (if needed) preprocess the
     * SPA data and/or to be reasonably sure we have a SPA packet (i.e
     * try to eliminate obvious non-spa packets).
    */
    //ÔÚÕâÀÎÒÃÇÒªÑéÖ¤ºÍÔ¤´¦ÀíSPAÊý¾Ý£¬ÌÞ³ýÃ÷ÏÔ²»ÊÇSPAµÄÊý¾Ý°ü¡£
    //ͨ¹ý±È¶ÔÕªÒª¼ì²éÊý¾ÝÍêÕûÐÔºÍÊÇ·ñÊܵ½ÖطŹ¥»÷¡£
    //¼ÆËãctx->encrypted_msgµÄSHA256ÕªÒªÊý¾Ý£¬´æÔÚraw_digest¡£
    if(!precheck_pkt(opts, spa_pkt, &spadat, &raw_digest))
        return;

    /* Now that we know there is a matching access.conf stanza and the
     * incoming SPA packet is not a replay, see if we should grant any
     * access
    */
    while(acc)
    {
        res = FKO_SUCCESS;
        cmd_exec_success  = 0;
        attempted_decrypt = 0;
        stanza_num++;

        /* Start access loop with a clean FKO context
        */
        //¶Ôÿһ¸östanza½øÐÐÆ¥Å䣬ֱµ½Æ¥Åä³É¹¦ÎªÖ¹¡£
        if(ctx != NULL)
        {
            if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                    spadat.pkt_source_ip, stanza_num
                );
            ctx = NULL;
        }

        /* Check for a match for the SPA source and destination IP and the access stanza
        */
        //Æ¥ÅästanzaÖеÄÔ´µØÖ·ºÍÄ¿µÄµØÖ·¡£
        if(! src_dst_check(acc, spa_pkt, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        log_msg(LOG_INFO,
            "(stanza #%d) SPA Packet from IP: %s received with access source match",
            stanza_num, spadat.pkt_source_ip);

        log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data);

        /* Make sure this access stanza has not expired
        */
        //È·±£Õâ¸östanzaûÓйýÆÚ¡£
        if(! check_stanza_expiration(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Get encryption type and try its decoding routine first (if the key
         * for that type is set)
        */
        //¼ì²â´Ë¶ÎÃÜÎĵļÓÃÜÀàÐÍ¡£
        enc_type = fko_encryption_type((char *)spa_pkt->packet_data);

		//»¹Ô­¼ÓÑÎÃÜÎÄ£¬Í¨¹ýHMACժҪУÑéÊý¾Ý°üµÄÍêÕûÐÔ£¬½âÃÜencrypted_msg¡£
        if(acc->use_rijndael)
            handle_rijndael_enc(acc, spa_pkt, &spadat, &ctx,
                        &attempted_decrypt, &cmd_exec_success, enc_type,
                        stanza_num, &res);

        if(! handle_gpg_enc(acc, spa_pkt, &spadat, &ctx, &attempted_decrypt,
                    cmd_exec_success, enc_type, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        if(! check_mode_ctx(&spadat, &ctx, attempted_decrypt,
                    enc_type, stanza_num, res))
        {
            acc = acc->next;
            continue;
        }

        /* Add this SPA packet into the replay detection cache
        */
        //¼ì²âÖطŹ¥»÷¡£
        if(! add_replay_cache(opts, acc, &spadat, raw_digest,
                    &added_replay_digest, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        /* At this point the SPA data is authenticated via the HMAC (if used
         * for now). Next we need to see if it meets our access criteria which
         * the server imposes regardless of the content of the SPA packet.
        */
        log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):",
            spadat.pkt_source_ip, stanza_num, res);

        res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf));
        if (res == FKO_SUCCESS)
            log_msg(LOG_DEBUG, "%s", dump_buf);
        else
            log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res));

        /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
         * then we need to make sure this incoming message is signer ID matches
         * an entry in the list.
        */

        if(! handle_gpg_sigs(acc, &spadat, &ctx, enc_type, stanza_num, &res))
        {
            acc = acc->next;
            continue;
        }

        /* Populate our spa data struct for future reference.
        */
        //ÏòspadatÖÐÌî³ä×ֶΡ£
        res = get_spa_data_fields(ctx, &spadat);

        if(res != FKO_SUCCESS)
        {
            log_msg(LOG_ERR,
                "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        /* Figure out what our timeout will be. If it is specified in the SPA
         * data, then use that.  If not, try the FW_ACCESS_TIMEOUT from the
         * access.conf file (if there is one).  Otherwise use the default.
        */
        //²é¿´ÔÚSPAÖÐÓÐûÓÐÉèÖó¬Ê±×ֶΣ¬·ñÔò¾ÍÓÃaccess.confÖÐÉèÖõÄ×ֶΡ£
        set_timeout(acc, &spadat);

        /* Check packet age if so configured.
        */
        //²é¿´Êý¾Ý°üÖеÄʱ¼ä´ÁÊÇ·ñ¹ýʱ¡£
        if(! check_pkt_age(opts, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* At this point, we have enough to check the embedded (or packet source)
         * IP address against the defined access rights.  We start by splitting
         * the spa msg source IP from the remainder of the message.
        */
        spa_ip_demark = strchr(spadat.spa_message, ',');
        if(spa_ip_demark == NULL)
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) Error parsing SPA message string: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1
                || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN)
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num);
            break;
        }

        strlcpy(spadat.spa_message_src_ip,
            spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);

        if(! is_valid_ipv4_addr(spadat.spa_message_src_ip))
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));
            break;
        }

        strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN);

        /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
         * is allowed.
        */
        if(! check_src_access(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
         * matches.
        */
        //Èç¹ûREQUIRE_USERNAME ×ֶα»ÉèÖã¬È·±£Óû§ÃûÔÚSPAÖпɱ»Æ¥Åä¡£
        if(! check_username(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Take action based on SPA message type.
        */
        if(! check_nat_access_types(opts, acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* Command messages.
        */
        if(acc->cmd_cycle_open != NULL)
        {
            if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res))
                break; /* successfully processed a matching access stanza */
            else
            {
                acc = acc->next;
                continue;
            }
        }
        else if(spadat.message_type == FKO_COMMAND_MSG)
        {
            if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res))
            {
                /* we processed the command on a matching access stanza, so we
                 * don't look for anything else to do with this SPA packet
                */
                break;
            }
            else
            {
                acc = acc->next;
                continue;
            }
        }

        /* From this point forward, we have some kind of access message. So
         * we first see if access is allowed by checking access against
         * restrict_ports and open_ports.
         *
         *  --DSS TODO: We should add BLACKLIST support here as well.
        */
        if(! check_port_proto(acc, &spadat, stanza_num))
        {
            acc = acc->next;
            continue;
        }

        /* At this point, we process the SPA request and break out of the
         * access stanza loop (first valid access stanza stops us looking
         * for others).
        */
        if(opts->test)  /* no firewall changes in --test mode */
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) --test mode enabled, skipping firewall manipulation.",
                spadat.pkt_source_ip, stanza_num
            );
            acc = acc->next;
            continue;
        }
        else
        {
            if(acc->cmd_cycle_open != NULL)
            {
            		//Ö´ÐÐiptablesµÄCMD¡£
                if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res))
                    break; /* successfully processed a matching access stanza */
                else
                {
                    acc = acc->next;
                    continue;
                }
            }
            else
            {
                process_spa_request(opts, acc, &spadat);
            }
        }

        /* If we made it here, then the SPA packet was processed according
         * to a matching access.conf stanza, so we're done with this packet.
        */
        break;
    }

    if (raw_digest != NULL)
        free(raw_digest);

    if(ctx != NULL)
    {
        if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                spadat.pkt_source_ip, stanza_num
            );
        ctx = NULL;
    }

    return;
}
/* Rule Processing - Create an access request...
*/
int
process_spa_request(const fko_srv_options_t * const opts,
        const acc_stanza_t * const acc, spa_data_t * const spadat)
{
    char            nat_ip[MAX_IPV4_STR_LEN] = {0};
    unsigned int    nat_port = 0;
    unsigned int    fst_proto;
    unsigned int    fst_port;

    struct fw_chain * const in_chain   = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
    struct fw_chain * const out_chain  = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]);
    struct fw_chain * const fwd_chain  = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]);
    struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]);

    acc_port_list_t *port_list = NULL;
    acc_port_list_t *ple = NULL;

    char            *ndx = NULL;
    int             res = 0, is_err;
    time_t          now;
    unsigned int    exp_ts;

    /* Parse and expand our access message.
    */
    if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
    {
        /* technically we would already have exited with an error if there were
         * any memory allocation errors (see the add_port_list() function), but
         * for completeness...
        */
        free_acc_port_list(port_list);
        return res;
    }

    /* Start at the top of the proto-port list...
    */
    ple = port_list;

    /* Remember the first proto/port combo in case we need them
     * for NAT access requests.
    */
    fst_proto = ple->proto;
    fst_port  = ple->port;

    /* Set our expire time value.
    */
    time(&now);
    exp_ts = now + spadat->fw_access_timeout;

    /* deal with SPA packets that themselves request a NAT operation
    */
    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)
    {
        ndx = strchr(spadat->nat_access, ',');
        if(ndx != NULL)
        {
            strlcpy(nat_ip, spadat->nat_access, (ndx-spadat->nat_access)+1);
            if (! is_valid_ipv4_addr(nat_ip))
            {
                log_msg(LOG_INFO, "Invalid NAT IP in SPA message");
                free_acc_port_list(port_list);
                return res;
            }

            nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT,
                    NO_EXIT_UPON_ERR, &is_err);
            if(is_err != FKO_SUCCESS)
            {
                log_msg(LOG_INFO, "Invalid NAT port in SPA message");
                free_acc_port_list(port_list);
                res = is_err;
                return res;
            }
        }

        if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG)
        {
            ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip,
                (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP),
                fst_proto, nat_port, in_chain, exp_ts, now, "local NAT",
                spadat->spa_message_remain);
        }
        else if(strlen(fwd_chain->to_chain))
        {
            /* FORWARD access rule
            */
            forward_access_rule(opts, acc, fwd_chain, nat_ip,
                    nat_port, fst_proto, fst_port, spadat, exp_ts, now);
        }

        /* DNAT rule
        */
        if(strlen(dnat_chain->to_chain) && !acc->disable_dnat)
            dnat_rule(opts, acc, dnat_chain, nat_ip,
                    nat_port, fst_proto, fst_port, spadat, exp_ts, now);

        if(acc->force_snat)
            snat_rule(opts, acc, nat_ip, nat_port,
                    fst_proto, fst_port, spadat, exp_ts, now);
    }
    else if(acc->force_nat) /* handle force NAT scenarios */
    {
        strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip));
        nat_port = acc->force_nat_port;

        /* FORWARD access rule
        */
        if(strlen(fwd_chain->to_chain))
            forward_access_rule(opts, acc, fwd_chain, nat_ip,
                    nat_port, fst_proto, fst_port, spadat, exp_ts, now);

        /* DNAT rule
        */
        if(strlen(dnat_chain->to_chain) && !acc->disable_dnat)
            dnat_rule(opts, acc, dnat_chain, nat_ip,
                nat_port, fst_proto, fst_port, spadat, exp_ts, now);

        /* SNAT rule if required - we only allow this for FORCE_NAT
         * access stanzas for now until a new SPA packet type is added.
        */
        if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
            snat_rule(opts, acc, nat_ip, nat_port,
                    fst_proto, fst_port, spadat, exp_ts, now);
    }
    else /* Non-NAT request - this is the typical case. */
    {
        /* Create an access command for each proto/port for the source ip.
        */
        while(ple != NULL)
        {
            ipt_rule(opts, NULL, IPT_RULE_ARGS, spadat->use_src_ip,
                (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP),
                ple->proto, ple->port, in_chain, exp_ts, now, "access",
                spadat->spa_message_remain);

            /* We need to make a corresponding OUTPUT rule if out_chain target
             * is not NULL.
            */
            if(strlen(out_chain->to_chain))
            {
                ipt_rule(opts, NULL, IPT_OUT_RULE_ARGS, spadat->use_src_ip,
                    (fwc.use_destination ? spadat->pkt_destination_ip : IPT_ANY_IP),
                    ple->proto, ple->port, out_chain, exp_ts, now, "OUTPUT",
                    spadat->spa_message_remain);
            }
            ple = ple->next;
        }
    }

    /* Done with the port list for access rules.
    */
    free_acc_port_list(port_list);

    return(res);
}
Exemple #5
0
/* Process the SPA packet data
*/
void
incoming_spa(fko_srv_options_t *opts)
{
    /* Always a good idea to initialize ctx to null if it will be used
     * repeatedly (especially when using fko_new_with_data()).
    */
    fko_ctx_t       ctx = NULL;

    char            *spa_ip_demark, *gpg_id, *raw_digest = NULL;
    time_t          now_ts;
    int             res, status, ts_diff, enc_type, stanza_num=0;
    int             added_replay_digest = 0, pkt_data_len=0;
    int             is_err, cmd_exec_success = 0, attempted_decrypt = 0;
    int             conf_pkt_age = 0;
    char            dump_buf[CTX_DUMP_BUFSIZE];

    spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);

    /* This will hold our pertinent SPA data.
    */
    spa_data_t spadat;

    /* Loop through all access stanzas looking for a match
    */
    acc_stanza_t        *acc = opts->acc_stanzas;
    acc_string_list_t   *gpg_id_ndx;
    unsigned char        is_gpg_match = 0;

    inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
        spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));

    /* At this point, we want to validate and (if needed) preprocess the
     * SPA data and/or to be reasonably sure we have a SPA packet (i.e
     * try to eliminate obvious non-spa packets).
    */
    pkt_data_len = spa_pkt->packet_data_len;
    res = preprocess_spa_data(opts, spadat.pkt_source_ip);
    if(res != FKO_SUCCESS)
    {
        log_msg(LOG_DEBUG, "[%s] preprocess_spa_data() returned error %i: '%s' for incoming packet.",
            spadat.pkt_source_ip, res, get_errstr(res));
        return;
    }

    if(opts->foreground == 1 && opts->verbose > 2)
    {
        printf("[+] candidate SPA packet payload:\n");
        hex_dump(spa_pkt->packet_data, pkt_data_len);
    }

    if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
    {
        conf_pkt_age = strtol_wrapper(opts->config[CONF_MAX_SPA_PACKET_AGE],
                0, RCHK_MAX_SPA_PACKET_AGE, NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
        {
            log_msg(LOG_ERR, "[*] [%s] invalid MAX_SPA_PACKET_AGE", spadat.pkt_source_ip);
            return;
        }
    }

    if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
    {
        if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
        {
            /* Check for a replay attack
            */
            res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data);
            if(res != FKO_SUCCESS)
            {
                if (raw_digest != NULL)
                    free(raw_digest);
                return;
            }
            if (raw_digest == NULL)
                return;

            if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS)
            {
                free(raw_digest);
                return;
            }
        }
    }
    else
    {
        log_msg(LOG_WARNING,
            "No access data found for source IP: %s", spadat.pkt_source_ip
        );
        return;
    }

    /* Now that we know there is a matching access.conf stanza and the
     * incoming SPA packet is not a replay, see if we should grant any
     * access
    */
    while(acc)
    {
        res = FKO_SUCCESS;
        cmd_exec_success  = 0;
        attempted_decrypt = 0;
        stanza_num++;

        /* Start access loop with a clean FKO context
        */
        if(ctx != NULL)
        {
            if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                    spadat.pkt_source_ip, stanza_num, fko_errstr(res)
                );
            ctx = NULL;
        }

        /* Check for a match for the SPA source IP and the access stanza
        */
        if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)))
        {
            acc = acc->next;
            continue;
        }

        log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
            stanza_num, spadat.pkt_source_ip);

        log_msg(LOG_DEBUG, "SPA Packet: '%s'", spa_pkt->packet_data);

        /* Make sure this access stanza has not expired
        */
        if(acc->access_expire_time > 0)
        {
            if(acc->expired)
            {
                acc = acc->next;
                continue;
            }
            else
            {
                if(time(NULL) > acc->access_expire_time)
                {
                    log_msg(LOG_INFO, "[%s] (stanza #%d) Access stanza has expired",
                        spadat.pkt_source_ip, stanza_num);
                    acc->expired = 1;
                    acc = acc->next;
                    continue;
                }
            }
        }

        /* Get encryption type and try its decoding routine first (if the key
         * for that type is set)
        */
        enc_type = fko_encryption_type((char *)spa_pkt->packet_data);

        if(acc->use_rijndael)
        {
            if (acc->key == NULL)
            {
                log_msg(LOG_ERR,
                    "[%s] (stanza #%d) No KEY for RIJNDAEL encrypted messages",
                    spadat.pkt_source_ip, stanza_num
                );
                acc = acc->next;
                continue;
            }

            /* Command mode messages may be quite long
            */
            if(acc->enable_cmd_exec || enc_type == FKO_ENCRYPTION_RIJNDAEL)
            {
                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, acc->hmac_type);
                attempted_decrypt = 1;
                if(res == FKO_SUCCESS)
                    cmd_exec_success = 1;
            }
        }

        if(acc->use_gpg && enc_type == FKO_ENCRYPTION_GPG && cmd_exec_success == 0)
        {
            /* For GPG we create the new context without decrypting on the fly
             * so we can set some GPG parameters first.
            */
            if(acc->gpg_decrypt_pw != NULL || acc->gpg_allow_no_pw)
            {
                res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL,
                        0, FKO_ENC_MODE_ASYMMETRIC, acc->hmac_key,
                        acc->hmac_key_len, acc->hmac_type);

                if(res != FKO_SUCCESS)
                {
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) Error creating fko context (before decryption): %s",
                        spadat.pkt_source_ip, stanza_num, fko_errstr(res)
                    );
                    acc = acc->next;
                    continue;
                }

                /* Set whatever GPG parameters we have.
                */
                if(acc->gpg_exe != NULL)
                {
                    res = fko_set_gpg_exe(ctx, acc->gpg_exe);
                    if(res != FKO_SUCCESS)
                    {
                        log_msg(LOG_WARNING,
                            "[%s] (stanza #%d) Error setting GPG path %s: %s",
                            spadat.pkt_source_ip, stanza_num, acc->gpg_exe,
                            fko_errstr(res)
                        );
                        acc = acc->next;
                        continue;
                    }
                }

                if(acc->gpg_home_dir != NULL)
                {
                    res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
                    if(res != FKO_SUCCESS)
                    {
                        log_msg(LOG_WARNING,
                            "[%s] (stanza #%d) Error setting GPG keyring path to %s: %s",
                            spadat.pkt_source_ip, stanza_num, acc->gpg_home_dir,
                            fko_errstr(res)
                        );
                        acc = acc->next;
                        continue;
                    }
                }

                if(acc->gpg_decrypt_id != NULL)
                    fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);

                /* If GPG_REQUIRE_SIG is set for this acc stanza, then set
                 * the FKO context accordingly and check the other GPG Sig-
                 * related parameters. This also applies when REMOTE_ID is
                 * set.
                */
                if(acc->gpg_require_sig)
                {
                    fko_set_gpg_signature_verify(ctx, 1);

                    /* Set whether or not to ignore signature verification errors.
                    */
                    fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error);
                }
                else
                {
                    fko_set_gpg_signature_verify(ctx, 0);
                    fko_set_gpg_ignore_verify_error(ctx, 1);
                }

                /* Now decrypt the data.
                */
                res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw, 0);
                attempted_decrypt = 1;
            }
        }

        if(attempted_decrypt == 0)
        {
            log_msg(LOG_ERR,
                "(stanza #%d) No stanza encryption mode match for encryption type: %i.",
                stanza_num, enc_type);
            acc = acc->next;
            continue;
        }

        /* Do we have a valid FKO context?  Did the SPA decrypt properly?
        */
        if(res != FKO_SUCCESS)
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Error creating fko context: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            if(IS_GPG_ERROR(res))
                log_msg(LOG_WARNING, "[%s] (stanza #%d) - GPG ERROR: %s",
                    spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx));

            acc = acc->next;
            continue;
        }

        /* Add this SPA packet into the replay detection cache
        */
        if (added_replay_digest == 0
                && strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
        {

            res = add_replay(opts, raw_digest);
            if (res != SPA_MSG_SUCCESS)
            {
                log_msg(LOG_WARNING, "[%s] (stanza #%d) Could not add digest to replay cache",
                    spadat.pkt_source_ip, stanza_num);
                acc = acc->next;
                continue;
            }
            added_replay_digest = 1;
        }

        /* At this point, we assume the SPA data is valid.  Now we need to see
         * if it meets our access criteria.
        */
        log_msg(LOG_DEBUG, "[%s] (stanza #%d) SPA Decode (res=%i):",
            spadat.pkt_source_ip, stanza_num, res);

        res = dump_ctx_to_buffer(ctx, dump_buf, sizeof(dump_buf));
        if (res == FKO_SUCCESS)
            log_msg(LOG_DEBUG, "%s", dump_buf);
        else
            log_msg(LOG_WARNING, "Unable to dump FKO context: %s", fko_errstr(res));

        /* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
         * then we need to make sure this incoming message is signer ID matches
         * an entry in the list.
        */
        if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig)
        {
            res = fko_get_gpg_signature_id(ctx, &gpg_id);
            if(res != FKO_SUCCESS)
            {
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) Error pulling the GPG signature ID from the context: %s",
                    spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx));
                acc = acc->next;
                continue;
            }

            log_msg(LOG_INFO, "[%s] (stanza #%d) Incoming SPA data signed by '%s'.",
                spadat.pkt_source_ip, stanza_num, gpg_id);

            if(acc->gpg_remote_id != NULL)
            {
                is_gpg_match = 0;
                for(gpg_id_ndx = acc->gpg_remote_id_list;
                        gpg_id_ndx != NULL; gpg_id_ndx=gpg_id_ndx->next)
                {
                    res = fko_gpg_signature_id_match(ctx,
                            gpg_id_ndx->str, &is_gpg_match);
                    if(res != FKO_SUCCESS)
                    {
                        log_msg(LOG_WARNING,
                            "[%s] (stanza #%d) Error in GPG siganture comparision: %s",
                            spadat.pkt_source_ip, stanza_num, fko_gpg_errstr(ctx));
                        acc = acc->next;
                        continue;
                    }
                    if(is_gpg_match)
                        break;
                }

                if(! is_gpg_match)
                {
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
                        spadat.pkt_source_ip, stanza_num, gpg_id);
                    acc = acc->next;
                    continue;
                }
            }
        }

        /* Populate our spa data struct for future reference.
        */
        res = get_spa_data_fields(ctx, &spadat);

        /* Figure out what our timeout will be. If it is specified in the SPA
         * data, then use that.  If not, try the FW_ACCESS_TIMEOUT from the
         * access.conf file (if there is one).  Otherwise use the default.
        */
        if(spadat.client_timeout > 0)
            spadat.fw_access_timeout = spadat.client_timeout;
        else if(acc->fw_access_timeout > 0)
            spadat.fw_access_timeout = acc->fw_access_timeout;
        else
            spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;

        if(res != FKO_SUCCESS)
        {
            log_msg(LOG_ERR, "[%s] (stanza #%d) Unexpected error pulling SPA data from the context: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        /* Check packet age if so configured.
        */
        if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
        {
            time(&now_ts);

            ts_diff = abs(now_ts - spadat.timestamp);

            if(ts_diff > conf_pkt_age)
            {
                log_msg(LOG_WARNING, "[%s] (stanza #%d) SPA data time difference is too great (%i seconds).",
                    spadat.pkt_source_ip, stanza_num, ts_diff);

                acc = acc->next;
                continue;
            }
        }

        /* At this point, we have enough to check the embedded (or packet source)
         * IP address against the defined access rights.  We start by splitting
         * the spa msg source IP from the remainder of the message.
        */
        spa_ip_demark = strchr(spadat.spa_message, ',');
        if(spa_ip_demark == NULL)
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Error parsing SPA message string: %s",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            acc = acc->next;
            continue;
        }

        if((spa_ip_demark-spadat.spa_message) < MIN_IPV4_STR_LEN-1
                || (spa_ip_demark-spadat.spa_message) > MAX_IPV4_STR_LEN)
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            if(ctx != NULL)
            {
                if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                        spadat.pkt_source_ip, stanza_num, fko_errstr(res)
                    );
                ctx = NULL;
            }
            break;
        }

        strlcpy(spadat.spa_message_src_ip,
            spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);

        if(! is_valid_ipv4_addr(spadat.spa_message_src_ip))
        {
            log_msg(LOG_WARNING, "[%s] (stanza #%d) Invalid source IP in SPA message, ignoring SPA packet",
                spadat.pkt_source_ip, stanza_num, fko_errstr(res));

            if(ctx != NULL)
            {
                if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                        spadat.pkt_source_ip, stanza_num, fko_errstr(res)
                    );
                ctx = NULL;
            }
            break;
        }

        strlcpy(spadat.spa_message_remain, spa_ip_demark+1, MAX_DECRYPTED_SPA_LEN);

        /* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
         * is allowed.
        */
        if(strcmp(spadat.spa_message_src_ip, "0.0.0.0") == 0)
        {
            if(acc->require_source_address)
            {
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) Got 0.0.0.0 when valid source IP was required.",
                    spadat.pkt_source_ip, stanza_num
                );

                acc = acc->next;
                continue;
            }

            spadat.use_src_ip = spadat.pkt_source_ip;
        }
        else
            spadat.use_src_ip = spadat.spa_message_src_ip;

        /* If REQUIRE_USERNAME is set, make sure the username in this SPA data
         * matches.
        */
        if(acc->require_username != NULL)
        {
            if(strcmp(spadat.username, acc->require_username) != 0)
            {
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) Username in SPA data (%s) does not match required username: %s",
                    spadat.pkt_source_ip, stanza_num, spadat.username, acc->require_username
                );

                acc = acc->next;
                continue;
            }
        }

        /* Take action based on SPA message type.
        */
        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)
        {
#if FIREWALL_IPTABLES
            if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)!=0)
            {
                log_msg(LOG_WARNING,
                    "(stanza #%d) SPA packet from %s requested NAT access, but is not enabled",
                    stanza_num, spadat.pkt_source_ip
                );

                acc = acc->next;
                continue;
            }
#else
            log_msg(LOG_WARNING,
                "(stanza #%d) SPA packet from %s requested unsupported NAT access",
                stanza_num, spadat.pkt_source_ip
            );

            acc = acc->next;
            continue;
#endif
        }

        /* Command messages.
        */
        if(spadat.message_type == FKO_COMMAND_MSG)
        {
            if(!acc->enable_cmd_exec)
            {
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) SPA Command message are not allowed in the current configuration.",
                    spadat.pkt_source_ip, stanza_num
                );

                acc = acc->next;
                continue;
            }
            else
            {
                log_msg(LOG_INFO,
                    "[%s] (stanza #%d) Processing SPA Command message: command='%s'.",
                    spadat.pkt_source_ip, stanza_num, spadat.spa_message_remain
                );

                /* Do we need to become another user? If so, we call
                 * run_extcmd_as and pass the cmd_exec_uid.
                */
                if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
                {
                    log_msg(LOG_INFO, "[%s] (stanza #%d) Setting effective user to %s (UID=%i) before running command.",
                        spadat.pkt_source_ip, stanza_num, acc->cmd_exec_user, acc->cmd_exec_uid);

                    res = run_extcmd_as(acc->cmd_exec_uid,
                                        spadat.spa_message_remain, NULL, 0, 0);
                }
                else /* Just run it as we are (root that is). */
                    res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5);

                /* --DSS XXX: I have found that the status (and res for that
                 *            matter) have been unreliable indicators of the
                 *            actual exit status of some commands.  Not sure
                 *            why yet.  For now, we will take what we get.
                */
                status = WEXITSTATUS(res);

                if(opts->verbose > 1)
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) CMD_EXEC: command returned %i",
                        spadat.pkt_source_ip, stanza_num, status);

                if(status != 0)
                    res = SPA_MSG_COMMAND_ERROR;

                if(ctx != NULL)
                {
                    if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                        log_msg(LOG_WARNING,
                            "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                            spadat.pkt_source_ip, stanza_num, fko_errstr(res)
                        );
                    ctx = NULL;
                }

                /* we processed the command on a matching access stanza, so we
                 * don't look for anything else to do with this SPA packet
                */
                break;
            }
        }

        /* From this point forward, we have some kind of access message. So
         * we first see if access is allowed by checking access against
         * restrict_ports and open_ports.
         *
         *  --DSS TODO: We should add BLACKLIST support here as well.
        */
        if(! acc_check_port_access(acc, spadat.spa_message_remain))
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) One or more requested protocol/ports was denied per access.conf.",
                spadat.pkt_source_ip, stanza_num
            );

            acc = acc->next;
            continue;
        }

        /* At this point, we process the SPA request and break out of the
         * access stanza loop (first valid access stanza stops us looking
         * for others).
        */
        process_spa_request(opts, acc, &spadat);
        if(ctx != NULL)
        {
            if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) fko_destroy() could not zero out sensitive data buffer.",
                    spadat.pkt_source_ip, stanza_num
                );
            ctx = NULL;
        }
        break;
    }

    if (raw_digest != NULL)
        free(raw_digest);

    if(ctx != NULL)
    {
        if(fko_destroy(ctx) == FKO_ERROR_ZERO_OUT_DATA)
            log_msg(LOG_WARNING,
                "[%s] fko_destroy() could not zero out sensitive data buffer.",
                spadat.pkt_source_ip
            );
        ctx = NULL;
    }

    return;
}
static void snat_rule(const fko_srv_options_t * const opts,
        const acc_stanza_t * const acc,
        const char * const nat_ip,
        const unsigned int nat_port,
        const unsigned int fst_proto,
        const unsigned int fst_port,
        spa_data_t * const spadat,
        const unsigned int exp_ts,
        const time_t now)
{
    char     rule_buf[CMD_BUFSIZE] = {0};
    char     snat_target[SNAT_TARGET_BUFSIZE] = {0};
    struct   fw_chain *snat_chain = NULL;

    log_msg(LOG_DEBUG, "snat_rule() forward_all: %d, nat_ip: %s, nat_port: %d",
            acc->forward_all, nat_ip, nat_port);

    if(acc->forward_all)
    {
        /* Add SNAT or MASQUERADE rules.
        */
        if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip))
        {
            /* Using static SNAT */
            snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-source %s", acc->force_snat_ip);
        }
        else if(acc->force_snat && acc->force_masquerade)
        {
            /* Using MASQUERADE */
            snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, " ");
        }
        else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
            && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
        {
            /* Using static SNAT */
            snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-source %s", opts->config[CONF_SNAT_TRANSLATE_IP]);
        }
        else
        {
            /* Using MASQUERADE */
            snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1, " ");
        }

        memset(rule_buf, 0, CMD_BUFSIZE);

        snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_ALL_RULE_ARGS,
            snat_chain->table,
            spadat->use_src_ip,
            exp_ts,
            snat_chain->target,
            snat_target
        );

        ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip,
            NULL, ANY_PROTO, ANY_PORT, snat_chain, exp_ts, now,
            "SNAT ALL", "*/*");
    }
    else
    {
        /* Add SNAT or MASQUERADE rules.
        */
        if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip))
        {
            /* Using static SNAT */
            snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-source %s:%i", acc->force_snat_ip, fst_port);
        }
        else if(acc->force_snat && acc->force_masquerade)
        {
            /* Using MASQUERADE */
            snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-ports %i", fst_port);
        }
        else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
            && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
        {
            /* Using static SNAT */
            snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP],
                fst_port);
        }
        else
        {
            /* Using MASQUERADE */
            snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
            snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                "--to-ports %i", fst_port);
        }

        memset(rule_buf, 0, CMD_BUFSIZE);

        snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS,
            snat_chain->table,
            fst_proto,
            nat_ip,
            nat_port,
            exp_ts,
            snat_chain->target,
            snat_target
        );

        ipt_rule(opts, rule_buf, NULL, spadat->use_src_ip,
                NULL, fst_proto, nat_port, snat_chain, exp_ts, now, "SNAT",
                spadat->spa_message_remain);
    }
    return;
}
Exemple #7
0
/* Set defaults, and do sanity and bounds checks for the various options.
*/
static void
validate_options(fko_srv_options_t *opts)
{
    char tmp_path[MAX_PATH_LEN] = {0};

    /* If no conf dir is set in the config file, use the default.
    */
    if(opts->config[CONF_FWKNOP_CONF_DIR] == NULL)
        set_config_entry(opts, CONF_FWKNOP_CONF_DIR, DEF_CONF_DIR);

    /* If no access.conf path was specified on the command line or set in
     * the config file, use the default.
    */
    if(opts->config[CONF_ACCESS_FILE] == NULL)
        set_config_entry(opts, CONF_ACCESS_FILE, DEF_ACCESS_FILE);

    /* If the pid and digest cache files where not set in the config file or
     * via command-line, then grab the defaults. Start with RUN_DIR as the
     * files may depend on that.
    */
    if(opts->config[CONF_FWKNOP_RUN_DIR] == NULL)
        set_config_entry(opts, CONF_FWKNOP_RUN_DIR, DEF_RUN_DIR);

    if(opts->config[CONF_FWKNOP_PID_FILE] == NULL)
    {
        strlcpy(tmp_path, opts->config[CONF_FWKNOP_RUN_DIR], sizeof(tmp_path));

        if(tmp_path[strlen(tmp_path)-1] != '/')
            strlcat(tmp_path, "/", sizeof(tmp_path));

        strlcat(tmp_path, DEF_PID_FILENAME, sizeof(tmp_path));

        set_config_entry(opts, CONF_FWKNOP_PID_FILE, tmp_path);
    }

#if USE_FILE_CACHE
    if(opts->config[CONF_DIGEST_FILE] == NULL)
#else
    if(opts->config[CONF_DIGEST_DB_FILE] == NULL)
#endif
    {
        strlcpy(tmp_path, opts->config[CONF_FWKNOP_RUN_DIR], sizeof(tmp_path));

        if(tmp_path[strlen(tmp_path)-1] != '/')
            strlcat(tmp_path, "/", sizeof(tmp_path));


#if USE_FILE_CACHE
        strlcat(tmp_path, DEF_DIGEST_CACHE_FILENAME, sizeof(tmp_path));
        set_config_entry(opts, CONF_DIGEST_FILE, tmp_path);
#else
        strlcat(tmp_path, DEF_DIGEST_CACHE_DB_FILENAME, sizeof(tmp_path));
        set_config_entry(opts, CONF_DIGEST_DB_FILE, tmp_path);
#endif
    }

    /* Set remaining require CONF_ vars if they are not already set.  */

    /* PCAP capture interface - note that if '-r <pcap file>' is specified
     * on the command line, then this will override the pcap interface setting.
    */
    if(opts->config[CONF_PCAP_INTF] == NULL)
        set_config_entry(opts, CONF_PCAP_INTF, DEF_INTERFACE);

    /* PCAP Promiscuous mode.
    */
    if(opts->config[CONF_ENABLE_PCAP_PROMISC] == NULL)
        set_config_entry(opts, CONF_ENABLE_PCAP_PROMISC,
                         DEF_ENABLE_PCAP_PROMISC);

    /* The packet count argument to pcap_dispatch()
    */
    if(opts->config[CONF_PCAP_DISPATCH_COUNT] == NULL)
        set_config_entry(opts, CONF_PCAP_DISPATCH_COUNT,
                         DEF_PCAP_DISPATCH_COUNT);

    /* Microseconds to sleep between pcap loop iterations
    */
    if(opts->config[CONF_PCAP_LOOP_SLEEP] == NULL)
        set_config_entry(opts, CONF_PCAP_LOOP_SLEEP,
                         DEF_PCAP_LOOP_SLEEP);

    /* PCAP Filter.
    */
    if(opts->config[CONF_PCAP_FILTER] == NULL)
        set_config_entry(opts, CONF_PCAP_FILTER, DEF_PCAP_FILTER);

    /* Enable SPA packet aging unless we're getting packet data
     * directly from a pcap file
    */
    if(opts->config[CONF_ENABLE_SPA_PACKET_AGING] == NULL)
    {
        if(opts->config[CONF_PCAP_FILE] == NULL)
        {
            set_config_entry(opts, CONF_ENABLE_SPA_PACKET_AGING,
                             DEF_ENABLE_SPA_PACKET_AGING);
        }
        else
        {
            set_config_entry(opts, CONF_ENABLE_SPA_PACKET_AGING, "N");
        }
    }

    /* SPA packet age.
    */
    if(opts->config[CONF_MAX_SPA_PACKET_AGE] == NULL)
        set_config_entry(opts, CONF_MAX_SPA_PACKET_AGE,
                         DEF_MAX_SPA_PACKET_AGE);


    /* Enable digest persistence.
    */
    if(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE] == NULL)
        set_config_entry(opts, CONF_ENABLE_DIGEST_PERSISTENCE,
                         DEF_ENABLE_DIGEST_PERSISTENCE);

    /* Max sniff bytes.
    */
    if(opts->config[CONF_MAX_SNIFF_BYTES] == NULL)
        set_config_entry(opts, CONF_MAX_SNIFF_BYTES, DEF_MAX_SNIFF_BYTES);

#if FIREWALL_IPTABLES
    /* Enable IPT forwarding.
    */
    if(opts->config[CONF_ENABLE_IPT_FORWARDING] == NULL)
        set_config_entry(opts, CONF_ENABLE_IPT_FORWARDING,
                         DEF_ENABLE_IPT_FORWARDING);

    /* Enable IPT local NAT.
    */
    if(opts->config[CONF_ENABLE_IPT_LOCAL_NAT] == NULL)
        set_config_entry(opts, CONF_ENABLE_IPT_LOCAL_NAT,
                         DEF_ENABLE_IPT_LOCAL_NAT);

    /* Enable IPT SNAT.
    */
    if(opts->config[CONF_ENABLE_IPT_SNAT] == NULL)
        set_config_entry(opts, CONF_ENABLE_IPT_SNAT,
                         DEF_ENABLE_IPT_SNAT);

    /* Make sure we have a valid IP if SNAT is enabled
    */
    if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
    {
        /* Note that fw_config_init() will set use_masquerade if necessary
        */
        if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
        {
            if(! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
            {
                log_msg(LOG_ERR,
                        "Invalid IPv4 addr for SNAT_TRANSLATE_IP"
                       );
                clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
        }
    }

    /* Enable IPT OUTPUT.
    */
    if(opts->config[CONF_ENABLE_IPT_OUTPUT] == NULL)
        set_config_entry(opts, CONF_ENABLE_IPT_OUTPUT,
                         DEF_ENABLE_IPT_OUTPUT);

    /* Flush IPT at init.
    */
    if(opts->config[CONF_FLUSH_IPT_AT_INIT] == NULL)
        set_config_entry(opts, CONF_FLUSH_IPT_AT_INIT, DEF_FLUSH_IPT_AT_INIT);

    /* Flush IPT at exit.
    */
    if(opts->config[CONF_FLUSH_IPT_AT_EXIT] == NULL)
        set_config_entry(opts, CONF_FLUSH_IPT_AT_EXIT, DEF_FLUSH_IPT_AT_EXIT);

    /* IPT input access.
    */
    if(opts->config[CONF_IPT_INPUT_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_INPUT_ACCESS,
                         DEF_IPT_INPUT_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_INPUT_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_INPUT_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* IPT output access.
    */
    if(opts->config[CONF_IPT_OUTPUT_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_OUTPUT_ACCESS,
                         DEF_IPT_OUTPUT_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_OUTPUT_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_OUTPUT_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* IPT forward access.
    */
    if(opts->config[CONF_IPT_FORWARD_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_FORWARD_ACCESS,
                         DEF_IPT_FORWARD_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_FORWARD_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_FORWARD_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* IPT dnat access.
    */
    if(opts->config[CONF_IPT_DNAT_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_DNAT_ACCESS,
                         DEF_IPT_DNAT_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_DNAT_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_DNAT_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* IPT snat access.
    */
    if(opts->config[CONF_IPT_SNAT_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_SNAT_ACCESS,
                         DEF_IPT_SNAT_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_SNAT_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_SNAT_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* IPT masquerade access.
    */
    if(opts->config[CONF_IPT_MASQUERADE_ACCESS] == NULL)
        set_config_entry(opts, CONF_IPT_MASQUERADE_ACCESS,
                         DEF_IPT_MASQUERADE_ACCESS);

    if(validate_ipt_chain_conf(opts->config[CONF_IPT_MASQUERADE_ACCESS]) != 1)
    {
        log_msg(LOG_ERR,
                "Invalid IPT_MASQUERADE_ACCESS specification, see fwknopd.conf comments"
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    /* Check for the iptables 'comment' match at init time
    */
    if(opts->config[CONF_ENABLE_IPT_COMMENT_CHECK] == NULL)
        set_config_entry(opts, CONF_ENABLE_IPT_COMMENT_CHECK,
                         DEF_ENABLE_IPT_COMMENT_CHECK);

#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)
        set_config_entry(opts, CONF_IPFW_START_RULE_NUM,
                         DEF_IPFW_START_RULE_NUM);

    /* Set IPFW max rules.
    */
    if(opts->config[CONF_IPFW_MAX_RULES] == NULL)
        set_config_entry(opts, CONF_IPFW_MAX_RULES,
                         DEF_IPFW_MAX_RULES);

    /* Set IPFW active set number.
    */
    if(opts->config[CONF_IPFW_ACTIVE_SET_NUM] == NULL)
        set_config_entry(opts, CONF_IPFW_ACTIVE_SET_NUM,
                         DEF_IPFW_ACTIVE_SET_NUM);

    /* Set IPFW expire set number.
    */
    if(opts->config[CONF_IPFW_EXPIRE_SET_NUM] == NULL)
        set_config_entry(opts, CONF_IPFW_EXPIRE_SET_NUM,
                         DEF_IPFW_EXPIRE_SET_NUM);

    /* Set IPFW Dynamic rule expiry interval.
    */
    if(opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL] == NULL)
        set_config_entry(opts, CONF_IPFW_EXPIRE_PURGE_INTERVAL,
                         DEF_IPFW_EXPIRE_PURGE_INTERVAL);

    /* Set IPFW Dynamic rule expiry interval.
    */
    if(opts->config[CONF_IPFW_ADD_CHECK_STATE] == NULL)
        set_config_entry(opts, CONF_IPFW_ADD_CHECK_STATE,
                         DEF_IPFW_ADD_CHECK_STATE);

#elif FIREWALL_PF
    /* Set PF anchor name
    */
    if(opts->config[CONF_PF_ANCHOR_NAME] == NULL)
        set_config_entry(opts, CONF_PF_ANCHOR_NAME,
                         DEF_PF_ANCHOR_NAME);

    /* Set PF rule expiry interval.
    */
    if(opts->config[CONF_PF_EXPIRE_INTERVAL] == NULL)
        set_config_entry(opts, CONF_PF_EXPIRE_INTERVAL,
                         DEF_PF_EXPIRE_INTERVAL);

#elif FIREWALL_IPF
    /* --DSS Place-holder */

#endif /* FIREWALL type */

    /* GPG Home dir.
    */
    if(opts->config[CONF_GPG_HOME_DIR] == NULL)
        set_config_entry(opts, CONF_GPG_HOME_DIR, DEF_GPG_HOME_DIR);

    /* Enable SPA over HTTP.
    */
    if(opts->config[CONF_ENABLE_SPA_OVER_HTTP] == NULL)
        set_config_entry(opts, CONF_ENABLE_SPA_OVER_HTTP,
                         DEF_ENABLE_SPA_OVER_HTTP);

    /* Enable TCP server.
    */
    if(opts->config[CONF_ENABLE_TCP_SERVER] == NULL)
        set_config_entry(opts, CONF_ENABLE_TCP_SERVER, DEF_ENABLE_TCP_SERVER);

    /* TCP Server port.
    */
    if(opts->config[CONF_TCPSERV_PORT] == NULL)
        set_config_entry(opts, CONF_TCPSERV_PORT, DEF_TCPSERV_PORT);

    /* Syslog identity.
    */
    if(opts->config[CONF_SYSLOG_IDENTITY] == NULL)
        set_config_entry(opts, CONF_SYSLOG_IDENTITY, DEF_SYSLOG_IDENTITY);

    /* Syslog facility.
    */
    if(opts->config[CONF_SYSLOG_FACILITY] == NULL)
        set_config_entry(opts, CONF_SYSLOG_FACILITY, DEF_SYSLOG_FACILITY);


    /* Validate integer variable ranges
    */
    validate_int_var_ranges(opts);

    /* Some options just trigger some output of information, or trigger an
     * external function, but do not actually start fwknopd.  If any of those
     * are set, we can return here an skip the validation routines as all
     * other options will be ignored anyway.
     *
     * These are also mutually exclusive (for now).
    */
    if((opts->dump_config + opts->kill + opts->restart + opts->status) == 1)
        return;

    if((opts->dump_config + opts->kill + opts->restart + opts->status) > 1)
    {
        log_msg(LOG_ERR,
                "The -D, -K, -R, and -S options are mutually exclusive.  Pick only one."
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    if(opts->config[CONF_FIREWALL_EXE] == NULL)
    {
        log_msg(LOG_ERR,
                "[*] No firewall command executable is set. Please check FIREWALL_EXE in fwknopd.conf."
               );
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    return;
}
Exemple #8
0
/* Validate and in some cases preprocess/reformat the SPA data.  Return an
 * error code value if there is any indication the data is not valid spa data.
*/
static int
preprocess_spa_data(const fko_srv_options_t *opts, spa_pkt_info_t *spa_pkt, spa_data_t *spadat)
{

    char    *ndx = (char *)&(spa_pkt->packet_data);
    char    *xff;
    int      i, pkt_data_len = 0;

    pkt_data_len = spa_pkt->packet_data_len;

    /* At this point, we can reset the packet data length to 0.  This is our
     * indicator to the rest of the program that we do not have a current
     * spa packet to process (after this one that is).
    */
    spa_pkt->packet_data_len = 0;

    /* These two checks are already done in process_packet(), but this is a
     * defensive measure to run them again here
    */
    if(pkt_data_len < MIN_SPA_DATA_SIZE)
        return(SPA_MSG_BAD_DATA);

    if(pkt_data_len > MAX_SPA_PACKET_LEN)
        return(SPA_MSG_BAD_DATA);

    /* Ignore any SPA packets that contain the Rijndael or GnuPG prefixes
     * since an attacker might have tacked them on to a previously seen
     * SPA packet in an attempt to get past the replay check.  And, we're
     * no worse off since a legitimate SPA packet that happens to include
     * a prefix after the outer one is stripped off won't decrypt properly
     * anyway because libfko would not add a new one.
    */
    if(constant_runtime_cmp(ndx, B64_RIJNDAEL_SALT, B64_RIJNDAEL_SALT_STR_LEN) == 0)
        return(SPA_MSG_BAD_DATA);

    if(pkt_data_len > MIN_GNUPG_MSG_SIZE
            && constant_runtime_cmp(ndx, B64_GPG_PREFIX, B64_GPG_PREFIX_STR_LEN) == 0)
        return(SPA_MSG_BAD_DATA);

    /* Initialize X-Forwarded-For field */
    spadat->pkt_source_xff_ip[0] = '\0';

    /* Detect and parse out SPA data from an HTTP request. If the SPA data
     * starts with "GET /" and the user agent starts with "Fwknop", then
     * assume it is a SPA over HTTP request.
    */
    if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "Y", 1) == 0
      && strncasecmp(ndx, "GET /", 5) == 0
      && strstr(ndx, "User-Agent: Fwknop") != NULL)
    {
        /* This looks like an HTTP request, so let's see if we are
         * configured to accept such request and if so, find the SPA
         * data.
        */

        /* Process X-Forwarded-For header */

        xff = strcasestr(ndx, "X-Forwarded-For: ");

        if (xff != NULL) {
            xff += 17;

            for (i = 0; *xff != '\0'; i++)
                if (isspace(*xff))
                   *xff = '\0';
                else
                   xff++;

            xff -= i - 1;

            if (!is_valid_ipv4_addr(xff))
                log_msg(LOG_WARNING,
                "Error parsing X-Forwarded-For header: value '%s' is not an IP address",
                xff);
            else
                strlcpy(spadat->pkt_source_xff_ip, xff, i);
        }

        /* Now extract, adjust (convert characters translated by the fwknop
         * client), and reset the SPA message itself.
        */
        strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len);
        pkt_data_len -= 5;

        for(i=0; i<pkt_data_len; i++)
        {
            if(isspace(*ndx)) /* The first space marks the end of the req */
            {
                *ndx = '\0';
                break;
            }
            else if(*ndx == '-') /* Convert '-' to '+' */
                *ndx = '+';
            else if(*ndx == '_') /* Convert '_' to '/' */
                *ndx = '/';

            ndx++;
        }

        if(i < MIN_SPA_DATA_SIZE)
            return(SPA_MSG_BAD_DATA);

        spa_pkt->packet_data_len = pkt_data_len = i;
    }

    /* Require base64-encoded data
    */
    if(! is_base64(spa_pkt->packet_data, pkt_data_len))
        return(SPA_MSG_NOT_SPA_DATA);


    /* If we made it here, we have no reason to assume this is not SPA data.
     * The ultimate test will be whether the SPA data authenticates via an
     * HMAC anyway.
    */
    return(FKO_SUCCESS);
}
/* Rule Processing - Create an access request...
*/
int
process_spa_request(const fko_srv_options_t * const opts,
        const acc_stanza_t * const acc, spa_data_t * const spadat)
{
    char             nat_ip[MAX_IPV4_STR_LEN] = {0};
    char             snat_target[SNAT_TARGET_BUFSIZE] = {0};
    char             rule_buf[CMD_BUFSIZE] = {0};
    char            *ndx;

    unsigned int     nat_port = 0;

    acc_port_list_t *port_list = NULL;
    acc_port_list_t *ple = NULL;

    unsigned int    fst_proto;
    unsigned int    fst_port;

    struct fw_chain * const in_chain   = &(opts->fw_config->chain[IPT_INPUT_ACCESS]);
    struct fw_chain * const out_chain  = &(opts->fw_config->chain[IPT_OUTPUT_ACCESS]);
    struct fw_chain * const fwd_chain  = &(opts->fw_config->chain[IPT_FORWARD_ACCESS]);
    struct fw_chain * const dnat_chain = &(opts->fw_config->chain[IPT_DNAT_ACCESS]);
    struct fw_chain *snat_chain; /* We assign this later (if we need to). */

    int             res = 0, is_err, snat_chain_num = 0;
    time_t          now;
    unsigned int    exp_ts;

    /* Parse and expand our access message.
    */
    if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
    {
        /* technically we would already have exited with an error if there were
         * any memory allocation errors (see the add_port_list() function), but
         * for completeness...
        */
        free_acc_port_list(port_list);
        return res;
    }

    /* Start at the top of the proto-port list...
    */
    ple = port_list;

    /* Remember the first proto/port combo in case we need them
     * for NAT access requests.
    */
    fst_proto = ple->proto;
    fst_port  = ple->port;

    /* Set our expire time value.
    */
    time(&now);
    exp_ts = now + spadat->fw_access_timeout;

    /* 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) && !acc->force_nat)
    {

        /* Check to make sure that the jump rules exist for each
         * required chain
        */
        if(chain_exists(opts, IPT_INPUT_ACCESS) == 0)
            create_chain(opts, IPT_INPUT_ACCESS);

        if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0)
            add_jump_rule(opts, IPT_INPUT_ACCESS);

        if(strlen(out_chain->to_chain))
        {
            if(chain_exists(opts, IPT_OUTPUT_ACCESS) == 0)
                create_chain(opts, IPT_OUTPUT_ACCESS);

            if(jump_rule_exists(opts, IPT_OUTPUT_ACCESS) == 0)
                add_jump_rule(opts, IPT_OUTPUT_ACCESS);
        }

        /* Create an access command for each proto/port for the source ip.
        */
        while(ple != NULL)
        {
            memset(rule_buf, 0, CMD_BUFSIZE);

            snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS,
                in_chain->table,
                ple->proto,
                spadat->use_src_ip,
                ple->port,
                exp_ts,
                in_chain->target
            );

            if(rule_exists(opts, in_chain, rule_buf,
                        ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0)
            {
                if(create_rule(opts, in_chain->to_chain, rule_buf))
                {
                    log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
                        in_chain->to_chain, spadat->use_src_ip,
                        spadat->spa_message_remain, exp_ts
                    );

                    in_chain->active_rules++;

                    /* Reset the next expected expire time for this chain if it
                    * is warranted.
                    */
                    if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
                        in_chain->next_expire = exp_ts;
                }
            }

            /* If we have to make an corresponding OUTPUT rule if out_chain target
            * is not NULL.
            */
            if(strlen(out_chain->to_chain))
            {
                memset(rule_buf, 0, CMD_BUFSIZE);

                snprintf(rule_buf, CMD_BUFSIZE-1, IPT_OUT_RULE_ARGS,
                    out_chain->table,
                    ple->proto,
                    spadat->use_src_ip,
                    ple->port,
                    exp_ts,
                    out_chain->target
                );

                if(rule_exists(opts, out_chain, rule_buf,
                        ple->proto, spadat->use_src_ip, ple->port, exp_ts) == 0)
                {
                    if(create_rule(opts, out_chain->to_chain, rule_buf))
                    {
                        log_msg(LOG_INFO, "Added Rule in %s for %s, %s expires at %u",
                            out_chain->to_chain, spadat->use_src_ip,
                            spadat->spa_message_remain, exp_ts
                        );

                        out_chain->active_rules++;

                        /* Reset the next expected expire time for this chain if it
                        * is warranted.  */
                        if(out_chain->next_expire < now || exp_ts < out_chain->next_expire)
                            out_chain->next_expire = exp_ts;
                    }
                }
            }
            ple = ple->next;
        }
    }
    /* NAT requests... */
    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
      || acc->force_nat)
    {
        /* Parse out the NAT IP and Port components.
        */
        if(acc->force_nat)
        {
            strlcpy(nat_ip, acc->force_nat_ip, sizeof(nat_ip));
            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);
                if (! is_valid_ipv4_addr(nat_ip))
                {
                    log_msg(LOG_INFO, "Invalid NAT IP in SPA message");
                    free_acc_port_list(port_list);
                    return res;
                }

                nat_port = strtol_wrapper(ndx+1, 0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
                if(is_err != FKO_SUCCESS)
                {
                    log_msg(LOG_INFO, "Invalid NAT port in SPA message");
                    free_acc_port_list(port_list);
                    res = is_err;
                    return res;
                }
            }
        }

        if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG)
        {
            memset(rule_buf, 0, CMD_BUFSIZE);

            snprintf(rule_buf, CMD_BUFSIZE-1, IPT_RULE_ARGS,
                in_chain->table,
                fst_proto,
                spadat->use_src_ip,
                nat_port,
                exp_ts,
                in_chain->target
            );

            /* Check to make sure that the jump rules exist for each
             * required chain
            */
            if(chain_exists(opts, IPT_INPUT_ACCESS) == 0)
                create_chain(opts, IPT_INPUT_ACCESS);

            if(jump_rule_exists(opts, IPT_INPUT_ACCESS) == 0)
                add_jump_rule(opts, IPT_INPUT_ACCESS);

            if(rule_exists(opts, in_chain, rule_buf,
                        fst_proto, spadat->use_src_ip, nat_port, exp_ts) == 0)
            {
                if(create_rule(opts, in_chain->to_chain, rule_buf))
                {
                    log_msg(LOG_INFO, "Added Rule to %s for %s, %s expires at %u",
                        in_chain->to_chain, spadat->use_src_ip,
                        spadat->spa_message_remain, exp_ts
                    );

                    in_chain->active_rules++;

                    /* Reset the next expected expire time for this chain if it
                    * is warranted.
                    */
                    if(in_chain->next_expire < now || exp_ts < in_chain->next_expire)
                        in_chain->next_expire = exp_ts;
                }
            }
        }
        else if(strlen(fwd_chain->to_chain))
        {
            /* Make our FORWARD and NAT rules, and make sure the
             * required chain and jump rule exists
            */
            if(chain_exists(opts, IPT_FORWARD_ACCESS) == 0)
                create_chain(opts, IPT_FORWARD_ACCESS);

            if (jump_rule_exists(opts, IPT_FORWARD_ACCESS) == 0)
                add_jump_rule(opts, IPT_FORWARD_ACCESS);

            memset(rule_buf, 0, CMD_BUFSIZE);

            snprintf(rule_buf, CMD_BUFSIZE-1, IPT_FWD_RULE_ARGS,
                fwd_chain->table,
                fst_proto,
                spadat->use_src_ip,
                nat_ip,
                nat_port,
                exp_ts,
                fwd_chain->target
            );

            if(rule_exists(opts, fwd_chain, rule_buf, fst_proto,
                    spadat->use_src_ip, nat_port, exp_ts) == 0)
            {
                if(create_rule(opts, fwd_chain->to_chain, rule_buf))
                {
                    log_msg(LOG_INFO, "Added FORWARD Rule to %s for %s, %s expires at %u",
                        fwd_chain->to_chain, spadat->use_src_ip,
                        spadat->spa_message_remain, exp_ts
                    );

                    fwd_chain->active_rules++;

                    /* Reset the next expected expire time for this chain if it
                    * is warranted.
                    */
                    if(fwd_chain->next_expire < now || exp_ts < fwd_chain->next_expire)
                        fwd_chain->next_expire = exp_ts;
                }
            }
        }

        if(strlen(dnat_chain->to_chain))
        {
            /* Make sure the required chain and jump rule exist
            */
            if(chain_exists(opts, IPT_DNAT_ACCESS) == 0)
                create_chain(opts, IPT_DNAT_ACCESS);

            if (jump_rule_exists(opts, IPT_DNAT_ACCESS) == 0)
                add_jump_rule(opts, IPT_DNAT_ACCESS);

            memset(rule_buf, 0, CMD_BUFSIZE);

            snprintf(rule_buf, CMD_BUFSIZE-1, IPT_DNAT_RULE_ARGS,
                dnat_chain->table,
                fst_proto,
                spadat->use_src_ip,
                fst_port,
                exp_ts,
                dnat_chain->target,
                nat_ip,
                nat_port
            );

            if(rule_exists(opts, dnat_chain, rule_buf, fst_proto,
                        spadat->use_src_ip, fst_port, exp_ts) == 0)
            {
                if(create_rule(opts, dnat_chain->to_chain, rule_buf))
                {
                    log_msg(LOG_INFO, "Added DNAT Rule to %s for %s, %s expires at %u",
                        dnat_chain->to_chain, spadat->use_src_ip,
                        spadat->spa_message_remain, exp_ts
                    );

                    dnat_chain->active_rules++;

                    /* Reset the next expected expire time for this chain if it
                    * is warranted.
                    */
                    if(dnat_chain->next_expire < now || exp_ts < dnat_chain->next_expire)
                        dnat_chain->next_expire = exp_ts;
                }
            }
        }

        /* If SNAT (or MASQUERADE) is wanted, then we add those rules here as well.
        */
        if(acc->force_snat || strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1) == 0)
        {
            /* Add SNAT or MASQUERADE rules.
            */
            if(acc->force_snat && is_valid_ipv4_addr(acc->force_snat_ip))
            {
                /* Using static SNAT */
                snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
                snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                    "--to-source %s:%i", acc->force_snat_ip, fst_port);
                snat_chain_num = IPT_SNAT_ACCESS;
            }
            else if(acc->force_snat && acc->force_masquerade)
            {
                /* Using MASQUERADE */
                snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
                snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                    "--to-ports %i", fst_port);
                snat_chain_num = IPT_MASQUERADE_ACCESS;
            }
            else if((opts->config[CONF_SNAT_TRANSLATE_IP] != NULL)
                && is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
            {
                /* Using static SNAT */
                snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);
                snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                    "--to-source %s:%i", opts->config[CONF_SNAT_TRANSLATE_IP],
                    fst_port);
                snat_chain_num = IPT_SNAT_ACCESS;
            }
            else
            {
                /* Using MASQUERADE */
                snat_chain = &(opts->fw_config->chain[IPT_MASQUERADE_ACCESS]);
                snprintf(snat_target, SNAT_TARGET_BUFSIZE-1,
                    "--to-ports %i", fst_port);
                snat_chain_num = IPT_MASQUERADE_ACCESS;
            }

            if(chain_exists(opts, snat_chain_num) == 0)
                create_chain(opts, snat_chain_num);

            if(jump_rule_exists(opts, snat_chain_num) == 0)
                add_jump_rule(opts, snat_chain_num);

            memset(rule_buf, 0, CMD_BUFSIZE);

            snprintf(rule_buf, CMD_BUFSIZE-1, IPT_SNAT_RULE_ARGS,
                snat_chain->table,
                fst_proto,
                nat_ip,
                nat_port,
                exp_ts,
                snat_chain->target,
                snat_target
            );

            if(rule_exists(opts, snat_chain, rule_buf, fst_proto,
                        spadat->use_src_ip, nat_port, exp_ts) == 0)
            {
                if(create_rule(opts, snat_chain->to_chain, rule_buf))
                {
                    log_msg(LOG_INFO, "Added SNAT Rule to %s for %s, %s expires at %u",
                        snat_chain->to_chain, spadat->use_src_ip,
                        spadat->spa_message_remain, exp_ts
                    );

                    snat_chain->active_rules++;

                    /* Reset the next expected expire time for this chain if it
                    * is warranted.
                    */
                    if(snat_chain->next_expire < now || exp_ts < snat_chain->next_expire)
                            snat_chain->next_expire = exp_ts;
                }
            }
        }
    }

    /* Done with the port list for access rules.
    */
    free_acc_port_list(port_list);

    return(res);
}
int
fw_config_init(fko_srv_options_t * const opts)
{

    memset(&fwc, 0x0, sizeof(struct fw_config));

    /* Set our firewall exe command path (iptables in most cases).
    */
    strlcpy(fwc.fw_command, opts->config[CONF_FIREWALL_EXE], sizeof(fwc.fw_command));

    /* Pull the fwknop chain config info and setup our internal
     * config struct.  The IPT_INPUT is the only one that is
     * required. The rest are optional.
    */
    if(set_fw_chain_conf(IPT_INPUT_ACCESS, opts->config[CONF_IPT_INPUT_ACCESS]) != 1)
        return 0;

    /* The FWKNOP_OUTPUT_ACCESS requires ENABLE_IPT_OUTPUT_ACCESS be Y
    */
    if(strncasecmp(opts->config[CONF_ENABLE_IPT_OUTPUT], "Y", 1)==0)
        if(set_fw_chain_conf(IPT_OUTPUT_ACCESS, opts->config[CONF_IPT_OUTPUT_ACCESS]) != 1)
            return 0;

    /* The remaining access chains require ENABLE_IPT_FORWARDING = Y
    */
    if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1)==0)
    {
        if(set_fw_chain_conf(IPT_FORWARD_ACCESS, opts->config[CONF_IPT_FORWARD_ACCESS]) != 1)
            return 0;

        if(set_fw_chain_conf(IPT_DNAT_ACCESS, opts->config[CONF_IPT_DNAT_ACCESS]) != 1)
            return 0;

        /* SNAT (whichever mode) requires ENABLE_IPT_SNAT = Y
        */
        if(strncasecmp(opts->config[CONF_ENABLE_IPT_SNAT], "Y", 1)==0)
        {
            if(opts->config[CONF_SNAT_TRANSLATE_IP] == NULL
                    || ! is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
            {
                fwc.use_masquerade = 1;
                if(set_fw_chain_conf(IPT_MASQUERADE_ACCESS, opts->config[CONF_IPT_MASQUERADE_ACCESS]) != 1)
                    return 0;
            }
            else
            {
                if(is_valid_ipv4_addr(opts->config[CONF_SNAT_TRANSLATE_IP]))
                {
                    if(set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]) != 1)
                        return 0;
                }
                else
                {
                    return 0;
                }
            }
        }
    }

    /* Let us find it via our opts struct as well.
    */
    opts->fw_config = &fwc;

    return 1;
}