Ejemplo n.º 1
0
static void
validate_int_var_ranges(fko_srv_options_t *opts)
{
#if FIREWALL_IPFW
    int     is_err = FKO_SUCCESS;
#endif

    range_check(opts, "PCAP_LOOP_SLEEP", opts->config[CONF_PCAP_LOOP_SLEEP],
                1, RCHK_MAX_PCAP_LOOP_SLEEP);
    range_check(opts, "MAX_SPA_PACKET_AGE", opts->config[CONF_MAX_SPA_PACKET_AGE],
                1, RCHK_MAX_SPA_PACKET_AGE);
    range_check(opts, "MAX_SNIFF_BYTES", opts->config[CONF_MAX_SNIFF_BYTES],
                1, RCHK_MAX_SNIFF_BYTES);
    range_check(opts, "TCPSERV_PORT", opts->config[CONF_TCPSERV_PORT],
                1, RCHK_MAX_TCPSERV_PORT);

#if FIREWALL_IPFW
    range_check(opts, "IPFW_START_RULE_NUM", opts->config[CONF_IPFW_START_RULE_NUM],
                0, RCHK_MAX_IPFW_START_RULE_NUM);
    range_check(opts, "IPFW_MAX_RULES", opts->config[CONF_IPFW_MAX_RULES],
                1, RCHK_MAX_IPFW_MAX_RULES);
    range_check(opts, "IPFW_ACTIVE_SET_NUM", opts->config[CONF_IPFW_ACTIVE_SET_NUM],
                0, RCHK_MAX_IPFW_SET_NUM);
    range_check(opts, "IPFW_EXPIRE_SET_NUM", opts->config[CONF_IPFW_EXPIRE_SET_NUM],
                0, RCHK_MAX_IPFW_SET_NUM);
    range_check(opts, "IPFW_EXPIRE_PURGE_INTERVAL",
                opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL],
                1, RCHK_MAX_IPFW_PURGE_INTERVAL);

    /* Make sure the active and expire sets are not identical whenever
     * they are non-zero
    */
    if((strtol_wrapper(opts->config[CONF_IPFW_ACTIVE_SET_NUM],
                       0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err) > 0
            && strtol_wrapper(opts->config[CONF_IPFW_EXPIRE_SET_NUM],
                              0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err) > 0)
            && strtol_wrapper(opts->config[CONF_IPFW_ACTIVE_SET_NUM],
                              0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err)
            == strtol_wrapper(opts->config[CONF_IPFW_EXPIRE_SET_NUM],
                              0, RCHK_MAX_IPFW_SET_NUM, NO_EXIT_UPON_ERR, &is_err))
    {
        log_msg(LOG_ERR,
                "[*] Cannot set identical ipfw active and expire sets.");
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid integer conversion error.\n");
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

#elif FIREWALL_PF
    range_check(opts, "PF_EXPIRE_INTERVAL", opts->config[CONF_PF_EXPIRE_INTERVAL],
                1, RCHK_MAX_PF_EXPIRE_INTERVAL);

#endif /* FIREWALL type */

    return;
}
Ejemplo n.º 2
0
static int
parse_client_timeout(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx)
{
    int         is_err;

    if(  ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
    {
        if((*t_size = strlen(*ndx)) < 1)
            return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING);

        if (*t_size > MAX_SPA_MESSAGE_SIZE)
            return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG);

        /* Should be a number only.
        */
        if(strspn(*ndx, "0123456789") != *t_size)
            return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL);

        ctx->client_timeout = (unsigned int) strtol_wrapper(*ndx, 0,
                (2 << 15), NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
            return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL);
    }

    return FKO_SUCCESS;
}
Ejemplo n.º 3
0
static int
parse_msg_type(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx)
{
    int    is_err, remaining_fields;

    if((*t_size = strcspn(*ndx, ":")) < 1)
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING);

    if(*t_size > MAX_SPA_MESSAGE_TYPE_SIZE)
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG);

    strlcpy(tbuf, *ndx, *t_size+1);

    ctx->message_type = strtol_wrapper(tbuf, 0,
            FKO_LAST_MSG_TYPE-1, NO_EXIT_UPON_ERR, &is_err);

    if(is_err != FKO_SUCCESS)
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL);

    /* Now that we have a valid type, ensure that the total
     * number of SPA fields is also valid for the type
    */
    remaining_fields = num_fields(*ndx);

    switch(ctx->message_type)
    {
        /* optional server_auth + digest */
        case FKO_COMMAND_MSG:
        case FKO_ACCESS_MSG:
            if(remaining_fields > 2)
                return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS;
            break;

        /* nat or client timeout + optional server_auth + digest */
        case FKO_NAT_ACCESS_MSG:
        case FKO_LOCAL_NAT_ACCESS_MSG:
        case FKO_CLIENT_TIMEOUT_ACCESS_MSG:
            if(remaining_fields > 3)
                return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS;
            break;

        /* client timeout + nat + optional server_auth + digest */
        case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG:
        case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG:
            if(remaining_fields > 4)
                return FKO_ERROR_INVALID_DATA_DECODE_WRONG_NUM_FIELDS;
            break;

        default: /* Should not reach here */
            return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL);
    }

    *ndx += *t_size + 1;
    return FKO_SUCCESS;
}
Ejemplo n.º 4
0
/* Check to see if an integer variable has a value that is within a
 * specific range
*/
static void
range_check(fko_srv_options_t *opts, char *var, char *val, int low, int high)
{
    int     is_err;

    strtol_wrapper(val, low, high, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] var %s value '%s' not in the range %d-%d",
                var, val, low, high);
        clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
    }

    return;
}
Ejemplo n.º 5
0
static int
get_rand_port(fko_ctx_t ctx)
{
    char *rand_val = NULL;
    char  port_str[MAX_PORT_STR_LEN+1] = {0};
    int   tmpint, is_err;
    int   port     = 0;
    int   res      = 0;

    res = fko_get_rand_value(ctx, &rand_val);
    if(res != FKO_SUCCESS)
    {
        errmsg("get_rand_port(), fko_get_rand_value", res);
        return -1;
    }

    strlcpy(port_str, rand_val, sizeof(port_str));

    tmpint = strtol_wrapper(port_str, 0, -1, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_VERBOSITY_ERROR,
            "[*] get_rand_port(), could not convert rand_val str '%s', to integer",
            rand_val);
        return -1;
    }

    /* Convert to a random value between 1024 and 65535
    */
    port = (MIN_HIGH_PORT + (tmpint % (MAX_PORT - MIN_HIGH_PORT)));

    /* Force libfko to calculate a new random value since we don't want to
     * give anyone a hint (via the port value) about the contents of the
     * encrypted SPA data.
    */
    res = fko_set_rand_value(ctx, NULL);
    if(res != FKO_SUCCESS)
    {
        errmsg("get_rand_port(), fko_get_rand_value", res);
        return -1;
    }

    return port;
}
Ejemplo n.º 6
0
static pid_t
get_running_pid(const fko_srv_options_t *opts)
{
    int     op_fd, is_err, bytes_read = 0;
    char    buf[PID_BUFLEN] = {0};
    pid_t   rpid            = 0;


    if(verify_file_perms_ownership(opts->config[CONF_FWKNOP_PID_FILE]) != 1)
    {
        fprintf(stderr, "verify_file_perms_ownership() error\n");
        return(rpid);
    }

    op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY);

    if(op_fd == -1)
    {
        if((opts->foreground != 0) && (opts->verbose != 0))
            perror("Error trying to open PID file: ");
        return(rpid);
    }

    bytes_read = read(op_fd, buf, PID_BUFLEN);
    if (bytes_read > 0)
    {
        buf[PID_BUFLEN-1] = '\0';
        /* max pid value is configurable on Linux
        */
        rpid = (pid_t) strtol_wrapper(buf, 0, (2 << 30),
                NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
            rpid = 0;
    }
    else if (bytes_read < 0)
        perror("Error trying to read() PID file: ");

    close(op_fd);

    return(rpid);
}
Ejemplo n.º 7
0
static int
parse_timestamp(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx)
{
    int         is_err;

    if((*t_size = strcspn(*ndx, ":")) < 1)
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING);

    if (*t_size > MAX_SPA_TIMESTAMP_SIZE)
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG);

    strlcpy(tbuf, *ndx, *t_size+1);

    ctx->timestamp = (unsigned int) strtol_wrapper(tbuf,
            0, -1, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL);

    *ndx += *t_size + 1;

    return FKO_SUCCESS;
}
Ejemplo n.º 8
0
/* 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);
}
Ejemplo n.º 9
0
int
main(int argc, char **argv)
{
    int                 res, last_sig, rp_cache_count, is_err;
    char               *locale;
    pid_t               old_pid;

    fko_srv_options_t   opts;

    while(1)
    {
        /* Handle command line
        */
        config_init(&opts, argc, argv);

        /* Process any options that do their thing and exit.
        */

        /* Kill the currently running fwknopd?
        */
        if(opts.kill == 1)
        {
            old_pid = get_running_pid(&opts);

            if(old_pid > 0)
            {
                res    = kill(old_pid, SIGTERM);
                is_err = kill(old_pid, 0);

                if(res == 0 && is_err != 0)
                {
                    fprintf(stdout, "Killed fwknopd (pid=%i)\n", old_pid);
                    clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
                }
                else
                {
                    /* give a bit of time for process shutdown and check again
                    */
                    sleep(1);
                    is_err = kill(old_pid, 0);
                    if(is_err != 0)
                    {
                        fprintf(stdout, "Killed fwknopd (pid=%i) via SIGTERM\n",
                                old_pid);
                        clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
                    }
                    else
                    {
                        res    = kill(old_pid, SIGKILL);
                        is_err = kill(old_pid, 0);
                        if(res == 0 && is_err != 0)
                        {
                            fprintf(stdout,
                                    "Killed fwknopd (pid=%i) via SIGKILL\n",
                                    old_pid);
                            clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
                        }
                        else
                        {
                            sleep(1);
                            is_err = kill(old_pid, 0);
                            if(is_err != 0)
                            {
                                fprintf(stdout,
                                        "Killed fwknopd (pid=%i) via SIGKILL\n",
                                        old_pid);
                                clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
                            }
                            else
                            {
                                perror("Unable to kill fwknop: ");
                                clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
                            }
                        }
                    }
                }
            }
            else
            {
                fprintf(stderr, "No running fwknopd detected.\n");
                clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
        }

        /* Status of the currently running fwknopd?
        */
        if(opts.status == 1)
        {
            old_pid = write_pid_file(&opts);

            if(old_pid > 0)
            {
                fprintf(stdout, "Detected fwknopd is running (pid=%i).\n", old_pid);
                clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
            }
            else
            {
                fprintf(stdout, "No running fwknopd detected.\n");
                clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
        }

        /* Restart the currently running fwknopd?
        */
        if(opts.restart == 1 || opts.status == 1)
        {
            old_pid = get_running_pid(&opts);

            if(old_pid > 0)
            {
                res = kill(old_pid, SIGHUP);
                if(res == 0)
                {
                    fprintf(stdout, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
                    clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
                }
                else
                {
                    perror("Unable to send signal to fwknop: ");
                    clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
                }
            }
            else
            {
                fprintf(stdout, "No running fwknopd detected.\n");
                clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
        }

        /* Initialize logging.
        */
        init_logging(&opts);

        /* Update the verbosity level for the log module */
        log_set_verbosity(LOG_DEFAULT_VERBOSITY + opts.verbose);

#if HAVE_LOCALE_H
        /* Set the locale if specified.
        */
        if(opts.config[CONF_LOCALE] != NULL
          && strncasecmp(opts.config[CONF_LOCALE], "NONE", 4) != 0)
        {
            locale = setlocale(LC_ALL, opts.config[CONF_LOCALE]);

            if(locale == NULL)
            {
                log_msg(LOG_ERR,
                    "WARNING: Unable to set locale to '%s'.",
                    opts.config[CONF_LOCALE]
                );
            }
            else
            {
                log_msg(LOG_INFO,
                    "Locale set to '%s'.", opts.config[CONF_LOCALE]
                );
            }
        }
#endif

        /* Make sure we have a valid run dir and path leading to digest file
         * in case it configured to be somewhere other than the run dir.
        */
        check_dir_path((const char *)opts.config[CONF_FWKNOP_RUN_DIR], "Run", 0);

        /* Initialize the firewall rules handler based on the fwknopd.conf
         * file, but (for iptables firewalls) don't flush any rules or create
         * any chains yet.  This allows us to dump the current firewall rules
         * via fw_rules_dump() in --fw-list mode before changing around any rules
         * of an existing fwknopd process.
        */
        if(fw_config_init(&opts) != 1)
            clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);

        if(opts.fw_list == 1 || opts.fw_list_all == 1)
        {
            fw_dump_rules(&opts);
            clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
        }

        if(opts.fw_flush == 1)
        {
            fprintf(stdout, "Deleting any existing firewall rules...\n");
            clean_exit(&opts, FW_CLEANUP, EXIT_SUCCESS);
        }

        /* Process the access.conf file.
        */
        parse_access_file(&opts);

        /* Show config (including access.conf vars) and exit dump config was
         * wanted.
        */
        if(opts.dump_config == 1)
        {
            dump_config(&opts);
            dump_access_list(&opts);
            clean_exit(&opts, NO_FW_CLEANUP, EXIT_SUCCESS);
        }

        /* If we are a new process (just being started), proceed with normal
         * start-up.  Otherwise, we are here as a result of a signal sent to an
         * existing process and we want to restart.
        */
        if(get_running_pid(&opts) != getpid())
        {
            /* If foreground mode is not set, then fork off and become a daemon.
            * Otherwise, attempt to get the pid file lock and go on.
            */
            if(opts.foreground == 0)
            {
                daemonize_process(&opts);
            }
            else
            {
                old_pid = write_pid_file(&opts);
                if(old_pid > 0)
                {
                    fprintf(stderr,
                        "[*] An instance of fwknopd is already running: (PID=%i).\n", old_pid
                    );

                    clean_exit(&opts, NO_FW_CLEANUP, EXIT_FAILURE);
                }
                else if(old_pid < 0)
                {
                    fprintf(stderr, "[*] PID file error. The lock may not be effective.\n");
                }
            }

            log_msg(LOG_INFO, "Starting %s", MY_NAME);
        }
        else
        {
            log_msg(LOG_INFO, "Re-starting %s", MY_NAME);
        }

        if(opts.verbose > 1 && opts.foreground)
        {
            dump_config(&opts);
            dump_access_list(&opts);
        }

        /* Initialize the digest cache for replay attack detection (either
         * with dbm support or with the default simple cache file strategy)
         * if so configured.
        */
        if(strncasecmp(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
        {
            rp_cache_count = replay_cache_init(&opts);

            if(rp_cache_count < 0)
            {
                log_msg(LOG_WARNING,
                    "Error opening digest cache file. Incoming digests will not be remembered."
                );
                /* Destination points to heap memory, and is guaranteed to be
                 * at least two bytes large via validate_options(),
                 * DEF_ENABLE_DIGEST_PERSISTENCE, and set_config_entry()
                */
                strlcpy(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "N", 2);
            }

            if(opts.verbose)
                log_msg(LOG_ERR,
                    "Using Digest Cache: '%s' (entry count = %i)",
#if USE_FILE_CACHE
                    opts.config[CONF_DIGEST_FILE], rp_cache_count
#else
                    opts.config[CONF_DIGEST_DB_FILE], rp_cache_count
#endif
                );
        }

        /* Prepare the firewall - i.e. flush any old rules and (for iptables)
         * create fwknop chains.
        */
        if(fw_initialize(&opts) != 1)
            clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE);

        /* If the TCP server option was set, fire it up here.
        */
        if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0)
        {

            strtol_wrapper(opts.config[CONF_TCPSERV_PORT],
                    1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
            if(is_err == FKO_SUCCESS)
            {
                run_tcp_server(&opts);
            }
            else
            {
                log_msg(LOG_WARNING,
                    "WARNING: ENABLE_TCP_SERVER is set, but TCPSERV_PORT is not valid. TCP server not started!"
                );
            }
        }

        /* Intiate pcap capture mode...
        */
        pcap_capture(&opts);

        if(got_signal) {
            last_sig   = got_signal;
            got_signal = 0;

            if(got_sighup)
            {
                log_msg(LOG_WARNING, "Got SIGHUP.  Re-reading configs.");
                free_configs(&opts);
                kill(opts.tcp_server_pid, SIGTERM);
                usleep(1000000);
                got_sighup = 0;
            }
            else if(got_sigint)
            {
                log_msg(LOG_WARNING, "Got SIGINT.  Exiting...");
                got_sigint = 0;
                break;
            }
            else if(got_sigterm)
            {
                log_msg(LOG_WARNING, "Got SIGTERM.  Exiting...");
                got_sigterm = 0;
                break;
            }
            else
            {
                log_msg(LOG_WARNING,
                    "Got signal %i. No defined action but to exit.", last_sig);
                break;
            }
        }
        else if (opts.packet_ctr_limit > 0
            && opts.packet_ctr >= opts.packet_ctr_limit)
        {
            log_msg(LOG_INFO,
                "Packet count limit (%d) reached.  Exiting...",
                opts.packet_ctr_limit);
            break;
        }
        else    /* got_signal was not set (should be if we are here) */
        {
            log_msg(LOG_WARNING,
                "Capture ended without signal.  Exiting...");
            break;
        }
    }

    log_msg(LOG_INFO, "Shutting Down fwknopd.");

    /* Kill the TCP server (if we have one running).
    */
    if(opts.tcp_server_pid > 0)
    {
        log_msg(LOG_INFO, "Killing the TCP server (pid=%i)",
            opts.tcp_server_pid);

        kill(opts.tcp_server_pid, SIGTERM);

        /* --DSS XXX: This seems to be necessary if the tcp server
         *            was restarted by this program.  We need to
         *            investigate and fix this. For now, this works
         *            (it is kludgy, but does no harm afaik).
        */
        kill(opts.tcp_server_pid, SIGKILL);
    }

    /* Other cleanup.
    */
    fw_cleanup(&opts);
    free_logging();

#if USE_FILE_CACHE
    free_replay_list(&opts);
#endif

    free_configs(&opts);

    return(0);
}
Ejemplo n.º 10
0
/* Set NAT access string
*/
static int
set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const access_buf)
{
    char                nat_access_buf[MAX_LINE_LEN] = {0};
    char                tmp_access_port[MAX_PORT_STR_LEN+1] = {0}, *ndx = NULL;
    int                 access_port = 0, i = 0, is_err = 0;
    char                dst_ip_str[INET_ADDRSTRLEN] = {0};
    char                hostname[HOSTNAME_BUFSIZE] = {0};
    int                 port = 0;
    struct addrinfo     hints;

    memset(&hints, 0 , sizeof(hints));

    ndx = strchr(options->access_str, '/');
    if(ndx == NULL)
    {
        log_msg(LOG_VERBOSITY_ERROR, "[*] Expecting <proto>/<port> for -A arg.");
        return FKO_ERROR_INVALID_DATA;
    }
    ndx++;

    while(*ndx != '\0' && isdigit(*ndx) && i < MAX_PORT_STR_LEN)
    {
        tmp_access_port[i] = *ndx;
        ndx++;
        i++;
    }
    tmp_access_port[i] = '\0';

    access_port = strtol_wrapper(tmp_access_port, 1,
            MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_VERBOSITY_ERROR, "[*] Invalid port value '%d' for -A arg.",
                access_port);
        return FKO_ERROR_INVALID_DATA;
    }

    if (options->nat_local && options->nat_access_str[0] == 0x0)
    {
        snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
            options->spa_server_str, access_port);
    }

    if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0)
    {
        if (ipv4_str_has_port(options->nat_access_str))
        {
            snprintf(nat_access_buf, MAX_LINE_LEN, "%s",
                options->nat_access_str);
        }
        else
        {
            snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
                options->nat_access_str, access_port);
        }
    }

    /* Check if there is a hostname to resolve as an ip address in the NAT access buffer */
    if (is_hostname_str_with_port(nat_access_buf, hostname, sizeof(hostname), &port))
    {
        /* Speed up the name resolution by forcing ipv4 (AF_INET).
         * A NULL pointer could be used instead if there is no constraint.
         * Maybe when ipv6 support will be enable the structure could initialize the
         * family to either AF_INET or AF_INET6 */
        hints.ai_family = AF_INET;

        if (resolve_dst_addr(hostname, &hints,
                    dst_ip_str, sizeof(dst_ip_str), options) != 0)
        {
            log_msg(LOG_VERBOSITY_ERROR, "[*] Unable to resolve %s as an ip address",
                    hostname);
            return FKO_ERROR_INVALID_DATA;
        }

        snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
                dst_ip_str, port);
    }

    /* Nothing to resolve */
    else;

    if(options->nat_rand_port)
    {
        /* Must print to stdout what the random port is since
         * if not then the user will not which port will be
         * opened/NAT'd on the fwknopd side
        */
        log_msg(LOG_VERBOSITY_NORMAL,
                "[+] Randomly assigned port '%d' on: '%s' will grant access to: '%s'",
                options->nat_port, access_buf, nat_access_buf);
    }

    return fko_set_spa_nat_access(ctx, nat_access_buf);
}
Ejemplo n.º 11
0
/* The pcap capture routine.
*/
int
pcap_capture(fko_srv_options_t *opts)
{
    pcap_t              *pcap;
    char                errstr[PCAP_ERRBUF_SIZE] = {0};
    struct bpf_program  fp;
    int                 res;
    int                 pcap_errcnt = 0;
    int                 pending_break = 0;
    int                 promisc = 0;
    int                 set_direction = 1;
    int                 pcap_file_mode = 0;
    int                 status;
    int                 useconds;
    int                 pcap_dispatch_count;
    int                 max_sniff_bytes;
    int                 is_err;
    pid_t               child_pid;

#if FIREWALL_IPFW
    time_t              now;
#endif

    useconds = strtol_wrapper(opts->config[CONF_PCAP_LOOP_SLEEP],
            0, RCHK_MAX_PCAP_LOOP_SLEEP, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid PCAP_LOOP_SLEEP_value");
        clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
    }

    max_sniff_bytes = strtol_wrapper(opts->config[CONF_MAX_SNIFF_BYTES],
            0, RCHK_MAX_SNIFF_BYTES, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid MAX_SNIFF_BYTES");
        clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
    }

    /* Set promiscuous mode if ENABLE_PCAP_PROMISC is set to 'Y'.
    */
    if(strncasecmp(opts->config[CONF_ENABLE_PCAP_PROMISC], "Y", 1) == 0)
        promisc = 1;

    if(opts->config[CONF_PCAP_FILE] != NULL
            && opts->config[CONF_PCAP_FILE][0] != '\0')
        pcap_file_mode = 1;

    if(pcap_file_mode == 1) {
        log_msg(LOG_INFO, "Reading pcap file: %s",
            opts->config[CONF_PCAP_FILE]);

        pcap = pcap_open_offline(opts->config[CONF_PCAP_FILE], errstr);

        if(pcap == NULL)
        {
            log_msg(LOG_ERR, "[*] pcap_open_offline() error: %s",
                    errstr);
            clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
        }
    }
    else
    {
        log_msg(LOG_INFO, "Sniffing interface: %s",
            opts->config[CONF_PCAP_INTF]);

        pcap = pcap_open_live(opts->config[CONF_PCAP_INTF],
            max_sniff_bytes, promisc, 100, errstr
        );

        if(pcap == NULL)
        {
            log_msg(LOG_ERR, "[*] pcap_open_live() error: %s", errstr);
            clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
        }
    }

    /* Set pcap filters, if any.
    */
    if (opts->config[CONF_PCAP_FILTER][0] != '\0')
    {
        if(pcap_compile(pcap, &fp, opts->config[CONF_PCAP_FILTER], 1, 0) == -1)
        {
            log_msg(LOG_ERR, "[*] Error compiling pcap filter: %s",
                pcap_geterr(pcap)
            );
            clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
        }

        if(pcap_setfilter(pcap, &fp) == -1)
        {
            log_msg(LOG_ERR, "[*] Error setting pcap filter: %s",
                pcap_geterr(pcap)
            );
            clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
        }

        log_msg(LOG_INFO, "PCAP filter is: '%s'", opts->config[CONF_PCAP_FILTER]);

        pcap_freecode(&fp);
    }

    /* Determine and set the data link encapsulation offset.
    */
    switch(pcap_datalink(pcap)) {
        case DLT_EN10MB:
            opts->data_link_offset = 14;
            break;
#if defined(__linux__)
        case DLT_LINUX_SLL:
            opts->data_link_offset = 16;
            break;
#elif defined(__OpenBSD__)
        case DLT_LOOP:
            set_direction = 0;
            opts->data_link_offset = 4;
            break;
#endif
        case DLT_NULL:
            set_direction = 0;
            opts->data_link_offset = 4;
            break;
        default:
            opts->data_link_offset = 0;
            break;
    }

    /* We are only interested on seeing packets coming into the interface.
    */
    if ((opts->pcap_any_direction == 0)
            && (set_direction == 1) && (pcap_file_mode == 0)
            && (pcap_setdirection(pcap, PCAP_D_IN) < 0))
        if(opts->verbose)
            log_msg(LOG_WARNING, "[*] Warning: pcap error on setdirection: %s.",
                pcap_geterr(pcap));

    /* Set our pcap handle nonblocking mode.
     *
     * NOTE: This is simply set to 0 for now until we find a need
     *       to actually use this mode (which when set on a FreeBSD
     *       system, it silently breaks the packet capture).
    */
    if((pcap_file_mode == 0)
            && (pcap_setnonblock(pcap, DEF_PCAP_NONBLOCK, errstr)) == -1)
    {
        log_msg(LOG_ERR, "[*] Error setting pcap nonblocking to %i: %s",
            0, errstr
        );
        clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
    }

    pcap_dispatch_count = strtol_wrapper(opts->config[CONF_PCAP_DISPATCH_COUNT],
            0, RCHK_MAX_PCAP_DISPATCH_COUNT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid PCAP_DISPATCH_COUNT");
        clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
    }

    /* Initialize our signal handlers. You can check the return value for
     * the number of signals that were *not* set.  Those that were not set
     * will be listed in the log/stderr output.
    */
    if(set_sig_handlers() > 0)
        log_msg(LOG_ERR, "Errors encountered when setting signal handlers.");

    log_msg(LOG_INFO, "Starting fwknopd main event loop.");

    /* Jump into our home-grown packet cature loop.
    */
    while(1)
    {
        /* If we got a SIGCHLD and it was the tcp server, then handle it here.
        */
        if(got_sigchld)
        {
            if(opts->tcp_server_pid > 0)
            {
                child_pid = waitpid(0, &status, WNOHANG);

                if(child_pid == opts->tcp_server_pid)
                {
                    if(WIFSIGNALED(status))
                        log_msg(LOG_WARNING, "TCP server got signal: %i",  WTERMSIG(status));

                    log_msg(LOG_WARNING,
                        "TCP server exited with status of %i. Attempting restart.",
                        WEXITSTATUS(status)
                    );

                    opts->tcp_server_pid = 0;

                    /* Attempt to restart tcp server ? */
                    usleep(1000000);
                    run_tcp_server(opts);
                }
            }

            got_sigchld = 0;
        }

        /* Any signal except USR1, USR2, and SIGCHLD mean break the loop.
        */
        if(got_signal != 0)
        {
            if(got_sigint || got_sigterm || got_sighup)
            {
                pcap_breakloop(pcap);
                pending_break = 1;
            }
            else if(got_sigusr1 || got_sigusr2)
            {
                /* Not doing anything with these yet.
                */
                got_sigusr1 = got_sigusr2 = 0;
                got_signal = 0;
            }
            else
                got_signal = 0;
        }

        res = pcap_dispatch(pcap, pcap_dispatch_count,
            (pcap_handler)&process_packet, (unsigned char *)opts);

        /* Count processed packets
        */
        if(res > 0)
        {
            if(opts->foreground == 1 && opts->verbose > 2)
                log_msg(LOG_DEBUG, "pcap_dispatch() processed: %d packets", res);

            /* Count the set of processed packets (pcap_dispatch() return
             * value) - we use this as a comparison for --packet-limit regardless
             * of SPA packet validity at this point.
            */
            opts->packet_ctr += res;
            if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
            {
                log_msg(LOG_WARNING,
                    "* Incoming packet count limit of %i reached",
                    opts->packet_ctr_limit
                );

                pcap_breakloop(pcap);
                pending_break = 1;
            }
        }
        /* If there was an error, complain and go on (to an extent before
         * giving up).
        */
        else if(res == -1)
        {
            log_msg(LOG_ERR, "[*] Error from pcap_dispatch: %s",
                pcap_geterr(pcap)
            );

            if(pcap_errcnt++ > MAX_PCAP_ERRORS_BEFORE_BAIL)
            {
                log_msg(LOG_ERR, "[*] %i consecutive pcap errors.  Giving up",
                    pcap_errcnt
                );
                clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
            }
        }
        else if(pending_break == 1 || res == -2)
        {
            /* pcap_breakloop was called, so we bail. */
            log_msg(LOG_INFO, "Gracefully leaving the fwknopd event loop.");
            break;
        }
        else
            pcap_errcnt = 0;

        /* Check for any expired firewall rules and deal with them.
        */
        check_firewall_rules(opts);

#if FIREWALL_IPFW
        /* Purge expired rules that no longer have any corresponding
         * dynamic rules.
        */
        if(opts->fw_config->total_rules > 0)
        {
            time(&now);
            if(opts->fw_config->last_purge < (now - opts->fw_config->purge_interval))
            {
                ipfw_purge_expired_rules(opts);
                opts->fw_config->last_purge = now;
            }
        }
#endif

        usleep(useconds);
    }

    pcap_close(pcap);

    return(0);
}
Ejemplo n.º 12
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;
}
Ejemplo n.º 13
0
/* Decode the encoded SPA data.
*/
int
fko_decode_spa_data(fko_ctx_t ctx)
{
    char       *tbuf, *ndx, *tmp;
    int         t_size, i, is_err;

    if (! is_valid_encoded_msg_len(ctx->encoded_msg_len))
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGLEN_VALIDFAIL);

    /* Make sure there are no non-ascii printable chars
    */
    for (i=0; i < (int)strnlen(ctx->encoded_msg, MAX_SPA_ENCODED_MSG_SIZE); i++)
        if(isprint(ctx->encoded_msg[i]) == 0)
            return(FKO_ERROR_INVALID_DATA_DECODE_NON_ASCII);

    /* Make sure there are enough fields in the SPA packet
     * delimited with ':' chars
    */
    ndx = ctx->encoded_msg;
    for (i=0; i < MAX_SPA_FIELDS; i++)
    {
        if ((tmp = strchr(ndx, ':')) == NULL)
            break;

        ndx = tmp;
        ndx++;
    }

    if (i < MIN_SPA_FIELDS)
        return(FKO_ERROR_INVALID_DATA_DECODE_LT_MIN_FIELDS);

    t_size = strnlen(ndx, SHA512_B64_LEN+1);

    switch(t_size)
    {
        case MD5_B64_LEN:
            ctx->digest_type = FKO_DIGEST_MD5;
            ctx->digest_len  = MD5_B64_LEN;
            break;

        case SHA1_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA1;
            ctx->digest_len  = SHA1_B64_LEN;
            break;

        case SHA256_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA256;
            ctx->digest_len  = SHA256_B64_LEN;
            break;

        case SHA384_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA384;
            ctx->digest_len  = SHA384_B64_LEN;
            break;

        case SHA512_B64_LEN:
            ctx->digest_type = FKO_DIGEST_SHA512;
            ctx->digest_len  = SHA512_B64_LEN;
            break;

        default: /* Invalid or unsupported digest */
            return(FKO_ERROR_INVALID_DIGEST_TYPE);
    }

    if (ctx->encoded_msg_len - t_size < 0)
        return(FKO_ERROR_INVALID_DATA_DECODE_ENC_MSG_LEN_MT_T_SIZE);

    if(ctx->digest != NULL)
        free(ctx->digest);

    /* Copy the digest into the context and terminate the encoded data
     * at that point so the original digest is not part of the
     * encoded string.
    */
    ctx->digest = strdup(ndx);
    if(ctx->digest == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Zero out the rest of the encoded_msg bucket...
    */
    bzero((ndx-1), t_size);

    ctx->encoded_msg_len -= t_size+1;

    /* Make a tmp bucket for processing base64 encoded data and
     * other general use.
    */
    tbuf = malloc(FKO_ENCODE_TMP_BUF_SIZE);
    if(tbuf == NULL)
        return(FKO_ERROR_MEMORY_ALLOCATION);

    /* Can now verify the digest.
    */
    switch(ctx->digest_type)
    {
        case FKO_DIGEST_MD5:
            md5_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA1:
            sha1_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA256:
            sha256_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA384:
            sha384_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;

        case FKO_DIGEST_SHA512:
            sha512_base64(tbuf, (unsigned char*)ctx->encoded_msg, ctx->encoded_msg_len);
            break;
    }

    /* We give up here if the computed digest does not match the
     * digest in the message data.
    */
    if(constant_runtime_cmp(ctx->digest, tbuf, t_size) != 0)
    {
        free(tbuf);
        return(FKO_ERROR_DIGEST_VERIFICATION_FAILED);
    }

    /* Now we will work through the encoded data and extract (and base64-
     * decode where necessary), the SPA data fields and populate the context.
    */
    ndx = ctx->encoded_msg;

    /* The rand val data */
    if((t_size = strcspn(ndx, ":")) < FKO_RAND_VAL_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_RAND_MISSING);
    }

    if(ctx->rand_val != NULL)
        free(ctx->rand_val);

    ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1);
    if(ctx->rand_val == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }
    ctx->rand_val = strncpy(ctx->rand_val, ndx, FKO_RAND_VAL_SIZE);

    /* Jump to the next field (username).  We need to use the temp buffer
     * for the base64 decode step.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_MISSING);
    }

    if (t_size > MAX_SPA_USERNAME_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    if(ctx->username != NULL)
        free(ctx->username);

    ctx->username = malloc(t_size+1); /* Yes, more than we need */
    if(ctx->username == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    if(b64_decode(tbuf, (unsigned char*)ctx->username) < 0)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_DECODEFAIL);
    }
    if(validate_username(ctx->username) != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_USERNAME_VALIDFAIL);
    }

    /* Extract the timestamp value.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_MISSING);
    }

    if (t_size > MAX_SPA_TIMESTAMP_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    ctx->timestamp = (unsigned int) strtol_wrapper(tbuf,
            0, -1, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_TIMESTAMP_DECODEFAIL);
    }

    /* Extract the version string.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_MISSING);
    }

    if (t_size > MAX_SPA_VERSION_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_VERSION_TOOBIG);
    }

    if(ctx->version != NULL)
        free(ctx->version);

    ctx->version = malloc(t_size+1);
    if(ctx->version == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    strlcpy(ctx->version, ndx, t_size+1);

    /* Extract the message type value.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_MISSING);
    }

    if (t_size > MAX_SPA_MESSAGE_TYPE_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    ctx->message_type = strtol_wrapper(tbuf, 0,
            FKO_LAST_MSG_TYPE, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MSGTYPE_DECODEFAIL);
    }

    /* Extract the SPA message string.
    */
    ndx += t_size + 1;
    if((t_size = strcspn(ndx, ":")) < 1)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_MISSING);
    }

    if (t_size > MAX_SPA_MESSAGE_SIZE)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_TOOBIG);
    }

    strlcpy(tbuf, ndx, t_size+1);

    if(ctx->message != NULL)
        free(ctx->message);

    ctx->message = malloc(t_size+1); /* Yes, more than we need */
    if(ctx->message == NULL)
    {
        free(tbuf);
        return(FKO_ERROR_MEMORY_ALLOCATION);
    }

    if(b64_decode(tbuf, (unsigned char*)ctx->message) < 0)
    {
        free(tbuf);
        return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_DECODEFAIL);
    }

    if(ctx->message_type == FKO_COMMAND_MSG)
    {
        /* Require a message similar to: 1.2.3.4,<command>
        */
        if(validate_cmd_msg(ctx->message) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_MESSAGE_VALIDFAIL);
        }
    }
    else
    {
        /* Require a message similar to: 1.2.3.4,tcp/22
        */
        if(validate_access_msg(ctx->message) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_ACCESS_VALIDFAIL);
        }
    }

    /* Extract nat_access string if the message_type indicates so.
    */
    if(  ctx->message_type == FKO_NAT_ACCESS_MSG
      || ctx->message_type == FKO_LOCAL_NAT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
      || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
    {
        ndx += t_size + 1;
        if((t_size = strcspn(ndx, ":")) < 1)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_MISSING);
        }

        if (t_size > MAX_SPA_MESSAGE_SIZE)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_TOOBIG);
        }

        strlcpy(tbuf, ndx, t_size+1);

        if(ctx->nat_access != NULL)
            free(ctx->nat_access);

        ctx->nat_access = malloc(t_size+1); /* Yes, more than we need */
        if(ctx->nat_access == NULL)
        {
            free(tbuf);
            return(FKO_ERROR_MEMORY_ALLOCATION);
        }

        if(b64_decode(tbuf, (unsigned char*)ctx->nat_access) < 0)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_DECODEFAIL);
        }

        if(validate_nat_access_msg(ctx->nat_access) != FKO_SUCCESS)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_NATACCESS_VALIDFAIL);
        }
    }

    /* Now look for a server_auth string.
    */
    ndx += t_size + 1;
    if((t_size = strlen(ndx)) > 0)
    {
        if (t_size > MAX_SPA_MESSAGE_SIZE)
        {
            free(tbuf);
            return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_MISSING);
        }

        /* There is data, but what is it?
         * If the message_type does not have a timeout, assume it is a
         * server_auth field.
        */
        if(  ctx->message_type != FKO_CLIENT_TIMEOUT_ACCESS_MSG
          && ctx->message_type != FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
          && ctx->message_type != FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
        {
            strlcpy(tbuf, ndx, t_size+1);

            if(ctx->server_auth != NULL)
                free(ctx->server_auth);

            ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
            if(ctx->server_auth == NULL)
            {
                free(tbuf);
                return(FKO_ERROR_MEMORY_ALLOCATION);
            }

            if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_SRVAUTH_DECODEFAIL);
            }

            /* At this point we should be done.
            */
            free(tbuf);

            /* Call the context initialized.
            */
            ctx->initval = FKO_CTX_INITIALIZED;
            FKO_SET_CTX_INITIALIZED(ctx);

            return(FKO_SUCCESS);
        }

        /* If we are here then we may still have a server_auth string,
         * or a timeout, or both. So we look for a ':' delimiter.  If
         * it is there we have both, if not we check the message_type
         * again.
        */
        if(strchr(ndx, ':'))
        {
            t_size = strcspn(ndx, ":");

            if (t_size > MAX_SPA_MESSAGE_SIZE)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_TOOBIG);
            }

            /* Looks like we have both, so assume this is the 
            */
            strlcpy(tbuf, ndx, t_size+1);

            if(ctx->server_auth != NULL)
                free(ctx->server_auth);

            ctx->server_auth = malloc(t_size+1); /* Yes, more than we need */
            if(ctx->server_auth == NULL)
            {
                free(tbuf);
                return(FKO_ERROR_MEMORY_ALLOCATION);
            }

            if(b64_decode(tbuf, (unsigned char*)ctx->server_auth) < 0)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_EXTRA_DECODEFAIL);
            }

            ndx += t_size + 1;
        }

        /* Now we look for a timeout value if one is supposed to be there.
        */
        if(  ctx->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG
          || ctx->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG
          || ctx->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
        {
            if((t_size = strlen(ndx)) < 1)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_MISSING);
            }
            if (t_size > MAX_SPA_MESSAGE_SIZE)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_TOOBIG);
            }

            /* Should be a number only.
            */
            if(strspn(ndx, "0123456789") != t_size)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_VALIDFAIL);
            }

            ctx->client_timeout = (unsigned int) strtol_wrapper(ndx, 0,
                    (2 << 15), NO_EXIT_UPON_ERR, &is_err);
            if(is_err != FKO_SUCCESS)
            {
                free(tbuf);
                return(FKO_ERROR_INVALID_DATA_DECODE_TIMEOUT_DECODEFAIL);
            }
        }
    }

    /* Done with the tmp buffer.
    */
    free(tbuf);

    /* Call the context initialized.
    */
    ctx->initval = FKO_CTX_INITIALIZED;
    FKO_SET_CTX_INITIALIZED(ctx);

    return(FKO_SUCCESS);
}
Ejemplo n.º 14
0
int
run_udp_server(fko_srv_options_t *opts)
{
    int                 s_sock, sfd_flags, selval, pkt_len;
    int                 is_err, s_timeout, rv=1, chk_rm_all=0;
    int                 rules_chk_threshold;
    fd_set              sfd_set;
    struct sockaddr_in  saddr, caddr;
    struct timeval      tv;
    char                sipbuf[MAX_IPV4_STR_LEN] = {0};
    char                dgram_msg[MAX_SPA_PACKET_LEN+1] = {0};
    unsigned short      port;
    socklen_t           clen;

    port = strtol_wrapper(opts->config[CONF_UDPSERV_PORT],
                          1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] Invalid max UDPSERV_PORT value.");
        return -1;
    }
    s_timeout = strtol_wrapper(opts->config[CONF_UDPSERV_SELECT_TIMEOUT],
                               1, RCHK_MAX_UDPSERV_SELECT_TIMEOUT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] Invalid max UDPSERV_SELECT_TIMEOUT value.");
        return -1;
    }
    rules_chk_threshold = strtol_wrapper(opts->config[CONF_RULES_CHECK_THRESHOLD],
                                         0, RCHK_MAX_RULES_CHECK_THRESHOLD, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid RULES_CHECK_THRESHOLD");
        clean_exit(opts, FW_CLEANUP, EXIT_FAILURE);
    }

    log_msg(LOG_INFO, "Kicking off UDP server to listen on port %i.", port);

    /* Now, let's make a UDP server
    */
    if ((s_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        log_msg(LOG_ERR, "run_udp_server: socket() failed: %s",
                strerror(errno));
        return -1;
    }

    /* Make our main socket non-blocking so we don't have to be stuck on
     * listening for incoming datagrams.
    */
    if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0)
    {
        log_msg(LOG_ERR, "run_udp_server: fcntl F_GETFL error: %s",
                strerror(errno));
        close(s_sock);
        return -1;
    }

    sfd_flags |= O_NONBLOCK;

    if(fcntl(s_sock, F_SETFL, sfd_flags) < 0)
    {
        log_msg(LOG_ERR, "run_udp_server: fcntl F_SETFL error setting O_NONBLOCK: %s",
                strerror(errno));
        close(s_sock);
        return -1;
    }

    /* Construct local address structure */
    memset(&saddr, 0x0, sizeof(saddr));
    saddr.sin_family      = AF_INET;           /* Internet address family */
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    saddr.sin_port        = htons(port);       /* Local port */

    /* Bind to the local address */
    if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
    {
        log_msg(LOG_ERR, "run_udp_server: bind() failed: %s",
                strerror(errno));
        close(s_sock);
        return -1;
    }

    /* Initialize our signal handlers. You can check the return value for
     * the number of signals that were *not* set.  Those that were not set
     * will be listed in the log/stderr output.
    */
    if(set_sig_handlers() > 0)
        log_msg(LOG_ERR, "Errors encountered when setting signal handlers.");

    FD_ZERO(&sfd_set);

    /* Now loop and receive SPA packets
    */
    while(1)
    {
        if(sig_do_stop())
        {
            if(opts->verbose)
                log_msg(LOG_INFO,
                        "udp_server: terminating signal received, will stop.");
            break;
        }

        /* Check for any expired firewall rules and deal with them.
        */
        if(!opts->test)
        {
            if(rules_chk_threshold > 0)
            {
                opts->check_rules_ctr++;
                if ((opts->check_rules_ctr % rules_chk_threshold) == 0)
                {
                    chk_rm_all = 1;
                    opts->check_rules_ctr = 0;
                }
            }
            check_firewall_rules(opts, chk_rm_all);
            chk_rm_all = 0;
        }

        /* Initialize and setup the socket for select.
        */
        FD_SET(s_sock, &sfd_set);

        /* Set our select timeout to (500ms by default).
        */
        tv.tv_sec = 0;
        tv.tv_usec = s_timeout;

        selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv);

        if(selval == -1)
        {
            if(errno == EINTR)
            {
                /* restart loop but only after we check for a terminating
                 * signal above in sig_do_stop()
                */
                continue;
            }
            else
            {
                log_msg(LOG_ERR, "run_udp_server: select error socket: %s",
                        strerror(errno));
                rv = -1;
                break;
            }
        }

        if(selval == 0)
            continue;

        if(! FD_ISSET(s_sock, &sfd_set))
            continue;

        /* If we make it here then there is a datagram to process
        */
        clen = sizeof(caddr);

        pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN,
                           0, (struct sockaddr *)&caddr, &clen);

        dgram_msg[pkt_len] = 0x0;

        if(opts->verbose)
        {
            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, "udp_server: Got UDP datagram (%d bytes) from: %s",
                    pkt_len, sipbuf);
        }

        /* Expect the data to not be too large
        */
        if(pkt_len <= MAX_SPA_PACKET_LEN)
        {
            /* Copy the packet for SPA processing
            */
            strlcpy((char *)opts->spa_pkt.packet_data, dgram_msg, pkt_len+1);
            opts->spa_pkt.packet_data_len = pkt_len;
            opts->spa_pkt.packet_proto    = IPPROTO_UDP;
            opts->spa_pkt.packet_src_ip   = caddr.sin_addr.s_addr;
            opts->spa_pkt.packet_dst_ip   = saddr.sin_addr.s_addr;
            opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port);
            opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port);

            incoming_spa(opts);
        }

        memset(dgram_msg, 0x0, sizeof(dgram_msg));

        opts->packet_ctr += 1;
        if(opts->foreground == 1 && opts->verbose > 2)
            log_msg(LOG_DEBUG, "run_udp_server() processed: %d packets",
                    opts->packet_ctr);

        if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
        {
            log_msg(LOG_WARNING,
                    "* Incoming packet count limit of %i reached",
                    opts->packet_ctr_limit
                   );
            break;
        }

    } /* infinite while loop */

    close(s_sock);
    return rv;
}
Ejemplo n.º 15
0
/* 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);
}
Ejemplo n.º 16
0
/* Fork off and run a "dummy" TCP server. The return value is the PID of
 * the child process or -1 if there is a fork error.
*/
int
run_tcp_server(fko_srv_options_t *opts)
{
    pid_t               pid, ppid;
    int                 s_sock, c_sock, sfd_flags, clen, selval;
    int                 reuse_addr = 1, is_err;
    fd_set              sfd_set;
    struct sockaddr_in  saddr, caddr;
    struct timeval      tv;
    char                sipbuf[MAX_IPV4_STR_LEN] = {0};

    unsigned short      port;

    port = strtol_wrapper(opts->config[CONF_TCPSERV_PORT],
            0, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] Invalid max TCPSERV_PORT value.");
        exit(EXIT_FAILURE);
    }
    log_msg(LOG_INFO, "Kicking off TCP server to listen on port %i.", port);

    /* Fork off a child process to run the command and provide its outputs.
    */
    pid = fork();

    /* Non-zero pid means we are the parent or there was a fork error.
     * in either case we simply return that value to the caller.
    */
    if (pid != 0)
    {
        opts->tcp_server_pid = pid;
        return(pid);
    }

    /* Get our parent PID so we can periodically check for it. We want to 
     * know when it goes away so we can to.
    */
    ppid = getppid();

    /* We are the child.  The first thing to do is close our copy of the
     * parent PID file so we don't end up holding the lock if the parent
     * suffers a sudden death that doesn't take us out too.
    */
    close(opts->lock_fd);

    /* Now, let's make a TCP server
    */
    if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        log_msg(LOG_ERR, "run_tcp_server: socket() failed: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* So that we can re-bind to it without TIME_WAIT problems
    */
    if(setsockopt(s_sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)) == -1)
    {
        log_msg(LOG_ERR, "run_tcp_server: setsockopt error: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Make our main socket non-blocking so we don't have to be stuck on
     * listening for incoming connections.
    */
    if((sfd_flags = fcntl(s_sock, F_GETFL, 0)) < 0)
    {
        log_msg(LOG_ERR, "run_tcp_server: fcntl F_GETFL error: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    sfd_flags |= O_NONBLOCK;

    if(fcntl(s_sock, F_SETFL, sfd_flags) < 0)
    {
        log_msg(LOG_ERR, "run_tcp_server: fcntl F_SETFL error setting O_NONBLOCK: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Construct local address structure */
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family      = AF_INET;           /* Internet address family */
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    saddr.sin_port        = htons(port);       /* Local port */

    /* Bind to the local address */
    if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
    {
        log_msg(LOG_ERR, "run_tcp_server: bind() failed: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Mark the socket so it will listen for incoming connections
     * (but only one at a time)
    */
    if (listen(s_sock, 1) < 0)
    {
        log_msg(LOG_ERR, "run_tcp_server: listen() failed: %s",
            strerror(errno));
        exit(EXIT_FAILURE);
    }

    clen = sizeof(caddr);

    /* Now loop and accept and drop connections after the first packet or a
     * short timeout.
    */
    while(1)
    {
        /* Initialize and setup the socket for select.
        */
        FD_ZERO(&sfd_set);
        FD_SET(s_sock, &sfd_set);

        /* Set our select timeout to 200 ms.
        */
        tv.tv_sec = 0;
        tv.tv_usec = 200000;

        selval = select(s_sock+1, &sfd_set, NULL, NULL, &tv);

        if(selval == -1)
        {
            /* Select error - so kill the child and bail.
            */
            log_msg(LOG_ERR, "run_tcp_server: select error socket: %s",
                strerror(errno));
            exit(EXIT_FAILURE);
        }

        if(selval == 0)
        {
            /* Timeout - So we check to make sure our parent is still there by simply
             *           using kill(ppid, 0) and checking the return value.
            */
            if(kill(ppid, 0) != 0 && errno == ESRCH)
                exit(EXIT_FAILURE);

            continue;
        }

        /* Wait for a client to connect
        */
        if((c_sock = accept(s_sock, (struct sockaddr *) &caddr, (socklen_t *)&clen)) < 0)
        {
            log_msg(LOG_ERR, "run_tcp_server: accept() failed: %s",
                strerror(errno));
            exit(EXIT_FAILURE); /* Should this be fatal? */
        }

        if(opts->verbose)
        {
            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);
        }

        /* Though hacky and clunky, we just sleep for a second then
         * close the socket.  No need to read or write anything.  This
         * just gives the client a sufficient window to send their
         * request on this socket. In any case the socket is closed
         * after that time.
        */
        usleep(1000000);
        shutdown(c_sock, SHUT_RDWR);
        close(c_sock);
    }
}
Ejemplo n.º 17
0
static void options_parse(int argc, char *argv[])
{
  int option_index= 0;
  int option_rv;

  memcached_programs_help_st help_options[]=
  {
    {0},
  };

  static struct option long_options[]=
    {
      {(OPTIONSTRING)"version", no_argument, NULL, OPT_VERSION},
      {(OPTIONSTRING)"help", no_argument, NULL, OPT_HELP},
      {(OPTIONSTRING)"verbose", no_argument, &opt_verbose, OPT_VERBOSE},
      {(OPTIONSTRING)"debug", no_argument, &opt_verbose, OPT_DEBUG},
      {(OPTIONSTRING)"servers", required_argument, NULL, OPT_SERVERS},
      {(OPTIONSTRING)"flag", required_argument, NULL, OPT_FLAG},
      {(OPTIONSTRING)"expire", required_argument, NULL, OPT_EXPIRE},
      {(OPTIONSTRING)"set",  no_argument, NULL, OPT_SET},
      {(OPTIONSTRING)"add",  no_argument, NULL, OPT_ADD},
      {(OPTIONSTRING)"replace",  no_argument, NULL, OPT_REPLACE},
      {(OPTIONSTRING)"hash", required_argument, NULL, OPT_HASH},
      {(OPTIONSTRING)"binary", no_argument, NULL, OPT_BINARY},
      {(OPTIONSTRING)"username", required_argument, NULL, OPT_USERNAME},
      {(OPTIONSTRING)"password", required_argument, NULL, OPT_PASSWD},
      {0, 0, 0, 0},
    };

  while (1)
  {
    option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);

    if (option_rv == -1) break;

    switch (option_rv)
    {
    case 0:
      break;
    case OPT_BINARY:
      opt_binary = 1;
      break;
    case OPT_VERBOSE: /* --verbose or -v */
      opt_verbose = OPT_VERBOSE;
      break;
    case OPT_DEBUG: /* --debug or -d */
      opt_verbose = OPT_DEBUG;
      break;
    case OPT_VERSION: /* --version or -V */
      version_command(PROGRAM_NAME);
      break;
    case OPT_HELP: /* --help or -h */
      help_command(PROGRAM_NAME, PROGRAM_DESCRIPTION, long_options, help_options);
      break;
    case OPT_SERVERS: /* --servers or -s */
      opt_servers= strdup(optarg);
      break;
    case OPT_FLAG: /* --flag */
      {
        bool strtol_error;
        opt_flags= (uint32_t)strtol_wrapper(optarg, 16, &strtol_error);
        if (strtol_error == true)
        {
          fprintf(stderr, "Bad value passed via --flag\n");
          exit(1);
        }
        break;
      }
    case OPT_EXPIRE: /* --expire */
      {
        bool strtol_error;
        opt_expires= (time_t)strtol_wrapper(optarg, 16, &strtol_error);
        if (strtol_error == true)
        {
          fprintf(stderr, "Bad value passed via --flag\n");
          exit(1);
        }
      }
    case OPT_SET:
      opt_method= OPT_SET;
      break;
    case OPT_REPLACE:
      opt_method= OPT_REPLACE;
      break;
    case OPT_ADD:
      opt_method= OPT_ADD;
      break;
    case OPT_HASH:
      opt_hash= strdup(optarg);
      break;
    case OPT_USERNAME:
      opt_username= optarg;
      break;
    case OPT_PASSWD:
      opt_passwd= optarg;
      break;
   case '?':
      /* getopt_long already printed an error message. */
      exit(1);
    default:
      abort();
    }
  }
}
Ejemplo n.º 18
0
/* Initialize program configuration via config file and/or command-line
 * switches.
*/
void
config_init(fko_srv_options_t *opts, int argc, char **argv)
{
    int             cmd_arg, index, is_err;
    unsigned char   got_conf_file = 0, got_override_config = 0;

    char            override_file[MAX_LINE_LEN] = {0};
    char           *ndx, *cmrk;

    /* Zero out options and opts_track.
    */
    memset(opts, 0x00, sizeof(fko_srv_options_t));

    /* Set some preconfiguration options (i.e. build-time defaults)
    */
    set_preconfig_entries(opts);

    /* In case this is a re-config.
    */
    optind = 0;

    /* First, scan the command-line args for -h/--help or an alternate
     * configuration file. If we find an alternate config file, use it,
     * otherwise use the default.  We also grab any override config files
     * as well.
    */
    while ((cmd_arg = getopt_long(argc, argv,
                                  GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) {

        /* If help is wanted, give it and exit.
        */
        switch(cmd_arg) {
        case 'h':
            usage();
            clean_exit(opts, NO_FW_CLEANUP, EXIT_SUCCESS);
            break;

        /* Look for configuration file arg.
        */
        case 'c':
            set_config_entry(opts, CONF_CONFIG_FILE, optarg);
            got_conf_file++;

            /* If we already have the config_override option, we are done.
            */
            if(got_override_config > 0)
                break;

        /* Look for override configuration file arg.
        */
        case 'O':
            set_config_entry(opts, CONF_OVERRIDE_CONFIG, optarg);
            got_override_config++;

            /* If we already have the conf_file option, we are done.
            */
            if(got_conf_file > 0)
                break;
        }
    }

    /* If no alternate configuration file was specified, we use the
     * default.
    */
    if(opts->config[CONF_CONFIG_FILE] == NULL)
        set_config_entry(opts, CONF_CONFIG_FILE, DEF_CONFIG_FILE);

    /* Parse configuration file to populate any params not already specified
     * via command-line options.
    */
    parse_config_file(opts, opts->config[CONF_CONFIG_FILE]);

    /* If there are override configuration entries, process them
     * here.
    */
    if(opts->config[CONF_OVERRIDE_CONFIG] != NULL)
    {
        /* Make a copy of the override_config string so we can munge it.
        */
        strlcpy(override_file, opts->config[CONF_OVERRIDE_CONFIG], sizeof(override_file));

        ndx  = override_file;
        cmrk = strchr(ndx, ',');

        if(cmrk == NULL)
        {
            /* Only one to process...
            */
            parse_config_file(opts, ndx);

        } else {
            /* Walk the string pulling the next config override
             * at the comma delimiters.
            */
            while(cmrk != NULL) {
                *cmrk = '\0';
                parse_config_file(opts, ndx);
                ndx = cmrk + 1;
                cmrk = strchr(ndx, ',');
            }

            /* Process the last entry
            */
            parse_config_file(opts, ndx);
        }
    }

    /* Set up the verbosity level according to the value found in the
     * config files */
    if (opts->config[CONF_VERBOSE] != NULL)
    {
        opts->verbose = strtol_wrapper(opts->config[CONF_VERBOSE], 0, -1,
                                       NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
        {
            log_msg(LOG_ERR, "[*] VERBOSE value '%s' not in the range (>0)",
                    opts->config[CONF_VERBOSE]);
            clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
        }
    }

    /* Reset the options index so we can run through them again.
    */
    optind = 0;

    /* Last, but not least, we process command-line options (some of which
     * may override configuration file options.
    */
    while ((cmd_arg = getopt_long(argc, argv,
                                  GETOPTS_OPTION_STRING, cmd_opts, &index)) != -1) {

        switch(cmd_arg) {
        case 'a':
            set_config_entry(opts, CONF_ACCESS_FILE, optarg);
            break;
        case 'c':
            /* This was handled earlier */
            break;
        case 'C':
            opts->packet_ctr_limit = strtol_wrapper(optarg,
                                                    0, (2 << 30), NO_EXIT_UPON_ERR, &is_err);
            if(is_err != FKO_SUCCESS)
            {
                log_msg(LOG_ERR,
                        "[*] invalid -C packet count limit '%s'",
                        optarg);
                clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
            break;
        case 'd':
#if USE_FILE_CACHE
            set_config_entry(opts, CONF_DIGEST_FILE, optarg);
#else
            set_config_entry(opts, CONF_DIGEST_DB_FILE, optarg);
#endif
            break;
        case 'D':
            opts->dump_config = 1;
            break;
        case 'f':
            opts->foreground = 1;
            break;
        case FW_LIST:
            opts->fw_list = 1;
            break;
        case FW_LIST_ALL:
            opts->fw_list = 1;
            opts->fw_list_all = 1;
            break;
        case FW_FLUSH:
            opts->fw_flush = 1;
            break;
        case GPG_HOME_DIR:
            if (is_valid_dir(optarg))
            {
                set_config_entry(opts, CONF_GPG_HOME_DIR, optarg);
            }
            else
            {
                log_msg(LOG_ERR,
                        "[*] Directory '%s' could not stat()/does not exist?",
                        optarg);
                clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
            }
            break;
        case 'i':
            set_config_entry(opts, CONF_PCAP_INTF, optarg);
            break;
        case 'K':
            opts->kill = 1;
            break;
        case 'l':
            set_config_entry(opts, CONF_LOCALE, optarg);
            break;
        case 'O':
            /* This was handled earlier */
            break;
        case 'p':
            set_config_entry(opts, CONF_FWKNOP_PID_FILE, optarg);
            break;
        case 'P':
            set_config_entry(opts, CONF_PCAP_FILTER, optarg);
            break;
        case PCAP_FILE:
            set_config_entry(opts, CONF_PCAP_FILE, optarg);
            break;
        case ENABLE_PCAP_ANY_DIRECTION:
            opts->pcap_any_direction = 1;
            break;
        case ROTATE_DIGEST_CACHE:
            opts->rotate_digest_cache = 1;
            break;
        case 'R':
            opts->restart = 1;
            break;
        case 'S':
            opts->status = 1;
            break;
        /* Verbosity level */
        case 'v':
            opts->verbose++;
            break;
        case SYSLOG_ENABLE:
            opts->syslog_enable = 1;
            break;
        case 'V':
            fprintf(stdout, "fwknopd server %s\n", MY_VERSION);
            clean_exit(opts, NO_FW_CLEANUP, EXIT_SUCCESS);
            break;
        default:
            usage();
            clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
        }
    }

    /* Now that we have all of our options set, and we are actually going to
     * start fwknopd, we can validate them.
    */
    validate_options(opts);

    return;
}
Ejemplo n.º 19
0
static int
parse_url(char *res_url, struct url* url)
{
    char *s_ndx, *e_ndx;
    int  tlen, tlen_offset, port, is_err;

    /* https is not supported.
    */
    if(strncasecmp(res_url, "https", 5) == 0)
    {
        log_msg(LOG_VERBOSITY_ERROR, "[*] https is not yet supported for http-resolve-ip.");
        return(-1);
    }

    /* Strip off http:// portion if necessary
    */
    if(strncasecmp(res_url, "http://", 7) == 0)
        s_ndx = res_url + 7;
    else
        s_ndx = res_url;

    /* Look for a colon in case an alternate port was specified.
    */
    e_ndx = strchr(s_ndx, ':');
    if(e_ndx != NULL)
    {
        port = strtol_wrapper(e_ndx+1, 1, MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
        if(is_err != FKO_SUCCESS)
        {
            log_msg(LOG_VERBOSITY_ERROR,
                "[*] resolve-url port value is invalid, must be in [%d-%d]",
                1, MAX_PORT);
            return(-1);
        }

        snprintf(url->port, sizeof(url->port)-1, "%u", port);

        /* Get the offset we need to skip the port portion when we
         * extract the hostname part.
        */
        tlen_offset = strlen(url->port)+1;
    }
    else
    {
        strlcpy(url->port, "80", sizeof(url->port));
        tlen_offset = 0;
    }

    /* Get rid of any trailing slash
    */
    if(res_url[strlen(res_url)-1] == '/')
        res_url[strlen(res_url)-1] = '\0';

    e_ndx = strchr(s_ndx, '/');
    if(e_ndx == NULL)
        tlen = strlen(s_ndx)+1;
    else
        tlen = (e_ndx-s_ndx)+1;

    tlen -= tlen_offset;

    if(tlen > MAX_URL_HOST_LEN)
    {
        log_msg(LOG_VERBOSITY_ERROR, "resolve-url hostname portion is too large.");
        return(-1);
    }
    strlcpy(url->host, s_ndx, tlen);

    if(e_ndx != NULL)
    {
        if(strlen(e_ndx) > MAX_URL_PATH_LEN)
        {
            log_msg(LOG_VERBOSITY_ERROR, "resolve-url path portion is too large.");
            return(-1);
        }

        strlcpy(url->path, e_ndx, sizeof(url->path));
    }
    else
    {
        /* default to "GET /" if there isn't a more specific URL
        */
        strlcpy(url->path, "/", sizeof(url->path));
    }

    return(0);
}
Ejemplo n.º 20
0
/* Iterate over the configure firewall access chains and purge expired
 * firewall rules.
*/
void
check_firewall_rules(const fko_srv_options_t * const opts)
{
    char             exp_str[12]     = {0};
    char             rule_num_str[6] = {0};
    char            *ndx, *rn_start, *rn_end, *tmp_mark;

    int             i, res, rn_offset, rule_num, is_err;
    time_t          now, rule_exp, min_exp = 0;

    struct fw_chain *ch = opts->fw_config->chain;

    time(&now);

    /* Iterate over each chain and look for active rules to delete.
    */
    for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++)
    {
        /* If there are no active rules or we have not yet
         * reached our expected next expire time, continue.
        */
        if(ch[i].active_rules == 0 || ch[i].next_expire > now)
            continue;

        zero_cmd_buffers();

        rn_offset = 0;

        /* There should be a rule to delete.  Get the current list of
         * rules for this chain and delete the ones that are expired.
        */
        snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_LIST_RULES_ARGS,
            opts->fw_config->fw_command,
            ch[i].table,
            ch[i].to_chain
        );

        res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE,
                WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
        chop_newline(cmd_out);

        log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, cmd_out: %s)",
            cmd_buf, res, cmd_out);

        if(!EXTCMD_IS_SUCCESS(res))
        {
            log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
            continue;
        }

        log_msg(LOG_DEBUG, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out);

        ndx = strstr(cmd_out, EXPIRE_COMMENT_PREFIX);
        if(ndx == NULL)
        {
            /* we did not find an expected rule.
            */
            log_msg(LOG_ERR,
                "Did not find expire comment in rules list %i", i);

            if (ch[i].active_rules > 0)
                ch[i].active_rules--;

            continue;
        }

        /* walk the list and process rules as needed.
        */
        while (ndx != NULL) {
            /* Jump forward and extract the timestamp
            */
            ndx += strlen(EXPIRE_COMMENT_PREFIX);

            /* remember this spot for when we look for the next
             * rule.
            */
            tmp_mark = ndx;

            strlcpy(exp_str, ndx, sizeof(exp_str));
            rule_exp = (time_t)atoll(exp_str);

            if(rule_exp <= now)
            {
                /* Backtrack and get the rule number and delete it.
                */
                rn_start = ndx;
                while(--rn_start > cmd_out)
                {
                    if(*rn_start == '\n')
                        break;
                }

                if(*rn_start != '\n')
                {
                    /* This should not happen. But if it does, complain,
                     * decrement the active rule value, and go on.
                    */
                    log_msg(LOG_ERR,
                        "Rule parse error while finding rule line start in chain %i", i);

                    if (ch[i].active_rules > 0)
                        ch[i].active_rules--;

                    break;
                }
                rn_start++;

                rn_end = strchr(rn_start, ' ');
                if(rn_end == NULL)
                {
                    /* This should not happen. But if it does, complain,
                     * decrement the active rule value, and go on.
                    */
                    log_msg(LOG_ERR,
                        "Rule parse error while finding rule number in chain %i", i);

                    if (ch[i].active_rules > 0)
                        ch[i].active_rules--;

                    break;
                }

                strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1);

                rule_num = strtol_wrapper(rule_num_str, rn_offset, RCHK_MAX_IPT_RULE_NUM,
                        NO_EXIT_UPON_ERR, &is_err);
                if(is_err != FKO_SUCCESS)
                {
                    log_msg(LOG_ERR,
                        "Rule parse error while finding rule number in chain %i", i);

                    if (ch[i].active_rules > 0)
                        ch[i].active_rules--;

                    break;
                }

                zero_cmd_buffers();

                snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS,
                    opts->fw_config->fw_command,
                    ch[i].table,
                    ch[i].to_chain,
                    rule_num - rn_offset
                );

                res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE,
                        WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
                chop_newline(err_buf);

                log_msg(LOG_DEBUG, "check_firewall_rules() CMD: '%s' (res: %d, err: %s)",
                    cmd_buf, res, err_buf);

                if(EXTCMD_IS_SUCCESS(res))
                {
                    log_msg(LOG_INFO, "Removed rule %s from %s with expire time of %u",
                        rule_num_str, ch[i].to_chain, rule_exp
                    );

                    rn_offset++;

                    if (ch[i].active_rules > 0)
                        ch[i].active_rules--;
                }
                else
                    log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);

            }
            else
            {
                /* Track the minimum future rule expire time.
                */
                if(rule_exp > now)
                    min_exp = (min_exp < rule_exp) ? min_exp : rule_exp;
            }

            /* Push our tracking index forward beyond (just processed) _exp_
             * string so we can continue to the next rule in the list.
            */
            ndx = strstr(tmp_mark, EXPIRE_COMMENT_PREFIX);
        }

        /* Set the next pending expire time accordingly. 0 if there are no
         * more rules, or whatever the next expected (min_exp) time will be.
        */
        if(ch[i].active_rules < 1)
            ch[i].next_expire = 0;
        else if(min_exp)
            ch[i].next_expire = min_exp;
    }
}
Ejemplo n.º 21
0
static int
set_fw_chain_conf(const int type, const char * const conf_str)
{
    int i, j, is_err;
    char tbuf[MAX_LINE_LEN]  = {0};
    const char *ndx          = conf_str;

    char *chain_fields[FW_NUM_CHAIN_FIELDS];

    struct fw_chain *chain = &(fwc.chain[type]);

    if(conf_str == NULL)
    {
        log_msg(LOG_ERR, "[*] NULL conf_str");
        return 0;
    }

    chain->type = type;

    if(ndx != NULL)
        chain_fields[0] = tbuf;

    i = 0;
    j = 1;
    while(*ndx != '\0')
    {
        if(*ndx != ' ')
        {
            if(*ndx == ',')
            {
                tbuf[i] = '\0';
                chain_fields[j++] = &(tbuf[++i]);
            }
            else
                tbuf[i++] = *ndx;
        }
        if(*ndx != '\0'
                && *ndx != ' '
                && *ndx != ','
                && *ndx != '_'
                && isalnum(*ndx) == 0)
        {
            log_msg(LOG_ERR, "[*] Custom chain config parse error: "
                "invalid character '%c' for chain type %i, "
                "line: %s", *ndx, type, conf_str);
            return 0;
        }
        ndx++;
    }

    /* Sanity check - j should be the number of chain fields
     * (excluding the type).
    */
    if(j != FW_NUM_CHAIN_FIELDS)
    {
        log_msg(LOG_ERR, "[*] Custom chain config parse error: "
            "wrong number of fields for chain type %i, "
            "line: %s", type, conf_str);
        return 0;
    }

    /* Pull and set Target */
    strlcpy(chain->target, chain_fields[0], sizeof(chain->target));

    /* Pull and set Table */
    strlcpy(chain->table, chain_fields[1], sizeof(chain->table));

    /* Pull and set From_chain */
    strlcpy(chain->from_chain, chain_fields[2], sizeof(chain->from_chain));

    /* Pull and set Jump_rule_position */
    chain->jump_rule_pos = strtol_wrapper(chain_fields[3],
            0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid jump rule position in Line: %s",
            conf_str);
        return 0;
    }

    /* Pull and set To_chain */
    strlcpy(chain->to_chain, chain_fields[4], sizeof(chain->to_chain));

    /* Pull and set to_chain rule position */
    chain->rule_pos = strtol_wrapper(chain_fields[5],
            0, RCHK_MAX_IPT_RULE_NUM, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_ERR, "[*] invalid to_chain rule position in Line: %s",
            conf_str);
        return 0;
    }
    return 1;
}
Ejemplo n.º 22
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;
}
Ejemplo n.º 23
0
/* Set NAT access string
*/
static int
set_nat_access(fko_ctx_t ctx, fko_cli_options_t *options, const char * const access_buf)
{
    char                nat_access_buf[MAX_LINE_LEN] = {0};
    char                tmp_access_port[MAX_PORT_STR_LEN+1] = {0}, *ndx = NULL;
    int                 access_port = 0, i = 0, is_err = 0;
    char                hostname[HOSTNAME_BUFSIZE] = {0};
    int                 port = 0;
    struct addrinfo     hints;

    memset(&hints, 0 , sizeof(hints));

    ndx = strchr(options->access_str, '/');
    if(ndx == NULL)
    {
        log_msg(LOG_VERBOSITY_ERROR, "[*] Expecting <proto>/<port> for -A arg.");
        return FKO_ERROR_INVALID_DATA;
    }
    ndx++;

    while(*ndx != '\0' && isdigit(*ndx) && i < MAX_PORT_STR_LEN)
    {
        tmp_access_port[i] = *ndx;
        ndx++;
        i++;
    }
    tmp_access_port[i] = '\0';

    access_port = strtol_wrapper(tmp_access_port, 1,
            MAX_PORT, NO_EXIT_UPON_ERR, &is_err);
    if(is_err != FKO_SUCCESS)
    {
        log_msg(LOG_VERBOSITY_ERROR, "[*] Invalid port value '%d' for -A arg.",
                access_port);
        return FKO_ERROR_INVALID_DATA;
    }

    if (options->nat_local && options->nat_access_str[0] == 0x0)
    {
        snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
            options->spa_server_str, access_port);
    }

    if (nat_access_buf[0] == 0x0 && options->nat_access_str[0] != 0x0)
    {
        if (ipv4_str_has_port(options->nat_access_str))
        {
            snprintf(nat_access_buf, MAX_LINE_LEN, "%s",
                options->nat_access_str);
        }
        else
        {
            snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
                options->nat_access_str, access_port);
        }
    }

    /* Check if there is a hostname to resolve as an ip address in the NAT access buffer */
    if (is_hostname_str_with_port(nat_access_buf, hostname, sizeof(hostname), &port))
    {

        /* We now send the hostname, and resolve it server side */
        snprintf(nat_access_buf, MAX_LINE_LEN, "%s",
                options->nat_access_str);
    }

        /* assume just hostname */
    else
    {
        snprintf(nat_access_buf, MAX_LINE_LEN, NAT_ACCESS_STR_TEMPLATE,
            options->nat_access_str, access_port);

    }

    if(options->nat_rand_port)
    {
        /* Must print to stdout what the random port is since
         * if not then the user will not which port will be
         * opened/NAT'd on the fwknopd side
        */
        log_msg(LOG_VERBOSITY_NORMAL,
                "[+] Randomly assigned port '%d' on: '%s' will grant access to: '%s'",
                options->nat_port, access_buf, nat_access_buf);
    }

    return fko_set_spa_nat_access(ctx, nat_access_buf);
}