예제 #1
0
파일: fkomodule.c 프로젝트: g-reno/fwknop
/* get_gpg_signature_id
*/
static PyObject *
get_gpg_signature_id(PyObject *self, PyObject *args)
{
    fko_ctx_t ctx;
    char *gpg_signature_id;
    int res;

    if(!PyArg_ParseTuple(args, "k", &ctx))
        return NULL;

    res = fko_get_gpg_signature_id(ctx, &gpg_signature_id);

    if(res != FKO_SUCCESS)
    {
        PyErr_SetString(FKOError, fko_errstr(res));
        return NULL;
    }

    return Py_BuildValue("s", gpg_signature_id);
}
예제 #2
0
static int
handle_gpg_sigs(acc_stanza_t *acc, spa_data_t *spadat,
        fko_ctx_t *ctx, const int enc_type, const int stanza_num, int *res)
{
    char                *gpg_id, *gpg_fpr;
    acc_string_list_t   *gpg_id_ndx;
    acc_string_list_t   *gpg_fpr_ndx;
    unsigned char        is_gpg_match = 0;

    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));
            return 0;
        }

        *res = fko_get_gpg_signature_fpr(*ctx, &gpg_fpr);
        if(*res != FKO_SUCCESS)
        {
            log_msg(LOG_WARNING,
                "[%s] (stanza #%d) Error pulling the GPG fingerprint from the context: %s",
                spadat->pkt_source_ip, stanza_num, fko_gpg_errstr(*ctx));
            return 0;
        }

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

        /* prefer GnuPG fingerprint match if so configured
        */
        if(acc->gpg_remote_fpr != NULL)
        {
            is_gpg_match = 0;
            for(gpg_fpr_ndx = acc->gpg_remote_fpr_list;
                    gpg_fpr_ndx != NULL; gpg_fpr_ndx=gpg_fpr_ndx->next)
            {
                *res = fko_gpg_signature_fpr_match(*ctx,
                        gpg_fpr_ndx->str, &is_gpg_match);
                if(*res != FKO_SUCCESS)
                {
                    log_msg(LOG_WARNING,
                        "[%s] (stanza #%d) Error in GPG signature comparision: %s",
                        spadat->pkt_source_ip, stanza_num, fko_gpg_errstr(*ctx));
                    return 0;
                }
                if(is_gpg_match)
                    break;
            }
            if(! is_gpg_match)
            {
                log_msg(LOG_WARNING,
                    "[%s] (stanza #%d) Incoming SPA packet signed by: %s, but that fingerprint is not in the GPG_FINGERPRINT_ID list.",
                    spadat->pkt_source_ip, stanza_num, gpg_fpr);
                return 0;
            }
        }

        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 signature comparision: %s",
                        spadat->pkt_source_ip, stanza_num, fko_gpg_errstr(*ctx));
                    return 0;
                }
                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 in the GPG_REMOTE_ID list.",
                    spadat->pkt_source_ip, stanza_num, gpg_id);
                return 0;
            }
        }
    }
    return 1;
}
예제 #3
0
파일: incoming_spa.c 프로젝트: haron/fwknop
/* 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;
}
예제 #4
0
/* Process the SPA packet data
*/
int
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;
    time_t          now_ts;
    int             res, status, ts_diff, enc_type;

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

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

    /* Get the access.conf data for the stanza that matches this incoming
     * source IP address.
    */
    acc_stanza_t   *acc = acc_check_source(opts, spa_pkt->packet_src_ip);

    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).
    */
    res = preprocess_spa_data(opts, spadat.pkt_source_ip);
    if(res != FKO_SUCCESS)
        return(SPA_MSG_NOT_SPA_DATA);

    log_msg(LOG_INFO, "SPA Packet from IP: %s received.", spadat.pkt_source_ip);

    if(acc == NULL)
    {
        log_msg(LOG_WARNING,
            "No access data found for source IP: %s", spadat.pkt_source_ip
        );

        return(SPA_MSG_ACCESS_DENIED);
    }

    if(opts->verbose > 1)
        log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data);

    /* 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(enc_type == FKO_ENCRYPTION_RIJNDAEL)
    {
        if(acc->key != NULL)
            res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
        else 
        {
            log_msg(LOG_ERR,
                "No KEY for RIJNDAEL encrypted messages");
            return(SPA_MSG_FKO_CTX_ERROR);
        }
    }
    else if(enc_type == FKO_ENCRYPTION_GPG)
    {
        /* For GPG we create the new context without decrypting on the fly
         * so we can set some  GPG parameters first.
        */
        if(acc->gpg_decrypt_pw != NULL)
        {
            res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL);
            if(res != FKO_SUCCESS)
            {
                log_msg(LOG_WARNING,
                    "Error creating fko context (before decryption): %s",
                    fko_errstr(res)
                );
                return(SPA_MSG_FKO_CTX_ERROR);
            }

            /* Set whatever GPG parameters we have.
            */
            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,
                        "Error setting GPG keyring path to %s: %s",
                        acc->gpg_home_dir,
                        fko_errstr(res)
                    );
                    return(SPA_MSG_FKO_CTX_ERROR);
                }

            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);
        }
        else
        {
            log_msg(LOG_ERR,
                "No GPG_DECRYPT_PW for GPG encrypted messages");
            return(SPA_MSG_FKO_CTX_ERROR);
        }
    }
    else
    {
        log_msg(LOG_ERR, "Unable to determing encryption type. Got type=%i.",
            enc_type);
        return(SPA_MSG_FKO_CTX_ERROR);
    }

    /* Do we have a valid FKO context?
    */
    if(res != FKO_SUCCESS)
    {
        log_msg(LOG_WARNING, "Error creating fko context: %s",
            fko_errstr(res));

        if(IS_GPG_ERROR(res))
            log_msg(LOG_WARNING, " - GPG ERROR: %s",
                fko_gpg_errstr(ctx));

        goto clean_and_bail;
    }

    /* At this point, we assume the SPA data is valid.  Now we need to see
     * if it meets our access criteria.
    */
    if(opts->verbose > 2)
        log_msg(LOG_INFO, "SPA Decode (res=%i):\n%s", res, dump_ctx(ctx));

    /* 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, "Error pulling the GPG signature ID from the context: %s",
                fko_gpg_errstr(ctx));
            goto clean_and_bail;
        }

        if(opts->verbose)
        log_msg(LOG_INFO, "Incoming SPA data signed by '%s'.", gpg_id);

        if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id))
        {
            log_msg(LOG_WARNING,
                "Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
                gpg_id);
            goto clean_and_bail;
        }
    }

    /* Check for replays if so configured.
    */
    if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
    {
        res = replay_check(opts, ctx);
        if(res != 0) /* non-zero means we have seen this packet before. */
            goto clean_and_bail;
    }

    /* 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, "Unexpected error pulling SPA data from the context: %s",
            fko_errstr(res));
        res = SPA_MSG_ERROR;
        goto clean_and_bail;
    }

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

        ts_diff = now_ts - spadat.timestamp;

        if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
        {
            log_msg(LOG_WARNING, "SPA data is too old (%i seconds).",
                ts_diff);
            res = SPA_MSG_TOO_OLD;
            goto clean_and_bail;
        }
    }

    /* 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, "Error parsing SPA message string: %s",
            fko_errstr(res));
        res = SPA_MSG_ERROR;
        goto clean_and_bail;
    }

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

    /* 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,
                "Got 0.0.0.0 when valid source IP was required."
            );
            res = SPA_MSG_ACCESS_DENIED;
            goto clean_and_bail;
        }

        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,
                "Username in SPA data (%s) does not match required username: %s",
                spadat.username, acc->require_username
            );
            res = SPA_MSG_ACCESS_DENIED;
            goto clean_and_bail;
        }
    }

    /* Take action based on SPA message type.  */

    /* Command messages.
    */
    if(spadat.message_type == FKO_COMMAND_MSG)
    {
        if(!acc->enable_cmd_exec)
        {
            log_msg(LOG_WARNING,
                "SPA Command message are not allowed in the current configuration."
            );
            res = SPA_MSG_ACCESS_DENIED;
        }
        else
        {
            log_msg(LOG_INFO,
                "Processing SPA Command message: command='%s'.",
                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)
            {
                if(opts->verbose)
                    log_msg(LOG_INFO, "Setting effective user to %s (UID=%i) before running command.",
                        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 > 2)
                log_msg(LOG_WARNING,
                    "CMD_EXEC: command returned %i", status);

            if(status != 0)
                res = SPA_MSG_COMMAND_ERROR;
        }

        goto clean_and_bail;
    }

    /* 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,
            "One or more requested protocol/ports was denied per access.conf."
        );

        res = SPA_MSG_ACCESS_DENIED;

        goto clean_and_bail;       
    }

    /* At this point, we can process the SPA request.
    */
    res = process_spa_request(opts, &spadat);

clean_and_bail:
    if(ctx != NULL)
        fko_destroy(ctx);

    return(res);
}
예제 #5
0
파일: fko_util.c 프로젝트: vaygr/fwknop
/**
 * @brief Dump a FKO context to a buffer
 *
 * This function parses a FKO context and decodes each field to dump them to a
 * buffer in a comprehensible way.
 *
 * @param ctx           FKO context to dump
 * @param dump_buf      Buffer where to store the dump of the context
 * @param dump_buf_len  Number of bytes available in the dump_buf array
 *
 * @return a FKO error code. FKO_SUCCESS if successful.
 */
int
dump_ctx_to_buffer(fko_ctx_t ctx, char *dump_buf, size_t dump_buf_len)
{
    int         cp = 0;
    int         err = FKO_LAST_ERROR;

    char       *rand_val        = NULL;
    char       *username        = NULL;
    char       *version         = NULL;
    char       *spa_message     = NULL;
    char       *nat_access      = NULL;
    char       *server_auth     = NULL;
    char       *enc_data        = NULL;
    char       *hmac_data       = NULL;
    char       *spa_digest      = NULL;
#if HAVE_LIBGPGME
    char          *gpg_signer        = NULL;
    char          *gpg_recip         = NULL;
    char          *gpg_sig_id        = NULL;
    unsigned char  gpg_sig_verify    = 0;
    unsigned char  gpg_ignore_verify = 0;
    char          *gpg_sig_fpr       = NULL;
    char          *gpg_home_dir      = NULL;
    char          *gpg_exe           = NULL;
    int            gpg_sigsum        = -1;
    int            gpg_sig_stat      = -1;
#endif
    char       *spa_data         = NULL;
    char        digest_str[24]   = {0};
    char        hmac_str[24]     = {0};
    char        enc_mode_str[FKO_ENCRYPTION_MODE_BUFSIZE] = {0};

    time_t      timestamp       = 0;
    short       msg_type        = -1;
    short       digest_type     = -1;
    short       hmac_type       = -1;
    short       encryption_type = -1;
    int         encryption_mode = -1;
    int         client_timeout  = -1;

    /* Zero-ed the buffer */
    memset(dump_buf, 0, dump_buf_len);

    /* Make sure the FKO context is initialized before printing it */
    if(!CTX_INITIALIZED(ctx))
        err = FKO_ERROR_CTX_NOT_INITIALIZED;

    else
    {
        /* Parse the FKO context and collect data */
        RETURN_ON_FKO_ERROR(err, fko_get_rand_value(ctx, &rand_val));
        RETURN_ON_FKO_ERROR(err, fko_get_username(ctx, &username));
        RETURN_ON_FKO_ERROR(err, fko_get_timestamp(ctx, &timestamp));
        RETURN_ON_FKO_ERROR(err, fko_get_version(ctx, &version));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_message_type(ctx, &msg_type));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_message(ctx, &spa_message));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_nat_access(ctx, &nat_access));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_server_auth(ctx, &server_auth));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_client_timeout(ctx, &client_timeout));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_digest_type(ctx, &digest_type));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_hmac_type(ctx, &hmac_type));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_encryption_type(ctx, &encryption_type));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_encryption_mode(ctx, &encryption_mode));
        RETURN_ON_FKO_ERROR(err, fko_get_encoded_data(ctx, &enc_data));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_hmac(ctx, &hmac_data));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_digest(ctx, &spa_digest));
        RETURN_ON_FKO_ERROR(err, fko_get_spa_data(ctx, &spa_data));

#if HAVE_LIBGPGME
        if(encryption_mode == FKO_ENC_MODE_ASYMMETRIC)
        {
            /* Populate GPG variables
            */
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_signer(ctx, &gpg_signer));
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_recipient(ctx, &gpg_recip));
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_signature_verify(ctx, &gpg_sig_verify));
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_ignore_verify_error(ctx, &gpg_ignore_verify));
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_home_dir(ctx, &gpg_home_dir));
            RETURN_ON_FKO_ERROR(err, fko_get_gpg_exe(ctx, &gpg_exe));
            if(fko_get_gpg_signature_id(ctx, &gpg_sig_id) != FKO_SUCCESS)
                gpg_sig_id = NULL;
            if(fko_get_gpg_signature_summary(ctx, &gpg_sigsum) != FKO_SUCCESS)
                gpg_sigsum = -1;
            if(fko_get_gpg_signature_status(ctx, &gpg_sig_stat) != FKO_SUCCESS)
                gpg_sig_stat = -1;
            if(fko_get_gpg_signature_fpr(ctx, &gpg_sig_fpr) != FKO_SUCCESS)
                gpg_sig_fpr = NULL;
        }
#endif

        /* Convert the digest integer to a string */
        if (digest_inttostr(digest_type, digest_str, sizeof(digest_str)) != 0)
            return (FKO_ERROR_INVALID_DIGEST_TYPE);

        /* Convert the encryption mode integer to a string */
        if (enc_mode_inttostr(encryption_mode, enc_mode_str, sizeof(enc_mode_str)) != 0)
            return (FKO_ERROR_INVALID_ENCRYPTION_TYPE);

        /* Convert the HMAC digest integer to a string if a HMAC message is available */
        if (ctx->msg_hmac_len != 0)
        {
            if (hmac_digest_inttostr(hmac_type, hmac_str, sizeof(hmac_str)) != 0)
                return (FKO_ERROR_UNSUPPORTED_HMAC_MODE);
        }

        /* Fill in the buffer to dump */
        cp  = append_msg_to_buf(dump_buf,    dump_buf_len,    "SPA Field Values:\n=================\n");
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "   Random Value: %s\n", rand_val == NULL ? NULL_STRING : rand_val);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "       Username: %s\n", username == NULL ? NULL_STRING : username);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "      Timestamp: %u\n", (unsigned int) timestamp);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "    FKO Version: %s\n", version == NULL ? NULL_STRING : version);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "   Message Type: %i (%s)\n", msg_type, msg_type_inttostr(msg_type));
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " Message String: %s\n", spa_message == NULL ? NULL_STRING : spa_message);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "     Nat Access: %s\n", nat_access == NULL ? NULL_STRING : nat_access);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "    Server Auth: %s\n", server_auth == NULL ? NULL_STRING : server_auth);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " Client Timeout: %u\n", client_timeout);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "    Digest Type: %u (%s)\n", digest_type, digest_str);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "      HMAC Type: %u (%s)\n", hmac_type, hmac_type == 0 ? "None" : hmac_str);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "Encryption Type: %d (%s)\n", encryption_type, enc_type_inttostr(encryption_type));
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "Encryption Mode: %d (%s)\n", encryption_mode, enc_mode_str);
#if HAVE_LIBGPGME
        if(encryption_mode == FKO_ENC_MODE_ASYMMETRIC)
        {
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "     GPG signer: %s\n", gpg_signer == NULL ? NULL_STRING : gpg_signer);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "  GPG recipient: %s\n", gpg_recip == NULL ? NULL_STRING : gpg_recip);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " GPG sig verify: %s\n", gpg_sig_verify == 0 ? "No" : "Yes");
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " GPG ignore sig: %s\n", gpg_ignore_verify == 0 ? "No" : "Yes");
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "     GPG sig ID: %s\n", gpg_sig_id == NULL ? NULL_STRING : gpg_sig_id);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "    GPG sig fpr: %s\n", gpg_sig_fpr == NULL ? NULL_STRING : gpg_sig_fpr);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "GPG sig summary: %d\n", gpg_sigsum);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " GPG sig status: %d\n", gpg_sig_stat);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "   GPG home dir: %s\n", gpg_home_dir == NULL ? NULL_STRING : gpg_home_dir);
            cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "        GPG exe: %s\n", gpg_exe == NULL ? GPG_EXE : gpg_exe);
        }
#endif
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "   Encoded Data: %s\n", enc_data == NULL ? NULL_STRING : enc_data);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "SPA Data Digest: %s\n", spa_digest == NULL ? NULL_STRING : spa_digest);
        cp += append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, "           HMAC: %s\n", hmac_data == NULL ? NULL_STRING : hmac_data);
        append_msg_to_buf(dump_buf+cp, dump_buf_len-cp, " Final SPA Data: %s\n", spa_data);

        err = FKO_SUCCESS;
    }

    return (err);
}