snort 讀取規則選項

簡介

    snort規則選項的讀取不單單是使用框架中預約義的回調函數解析選項,更是預約義的回調函數不能處理時,調用用戶自定義的預處理或則探測處理解析參數初始化的時候(也有少許插件是在規則解析較上層完成的初始化).所以在整個規則解析中該部分是最複雜也是最重要的。
node

代碼分析

/***************************************************************/
static const RuleOptFunc rule_options[] =
{
    { RULE_OPT__ACTIVATED_BY,     1, 1, ParseOtnActivatedBy },
    { RULE_OPT__ACTIVATES,        1, 1, ParseOtnActivates },
    { RULE_OPT__CLASSTYPE,        1, 1, ParseOtnClassType },
    { RULE_OPT__COUNT,            1, 1, ParseOtnCount },
    { RULE_OPT__DETECTION_FILTER, 1, 1, ParseOtnDetectionFilter },
    { RULE_OPT__GID,              1, 1, ParseOtnGid },
    { RULE_OPT__LOGTO,            1, 1, ParseOtnLogTo },
    { RULE_OPT__METADATA,         1, 0, ParseOtnMetadata },
    { RULE_OPT__MSG,              1, 1, ParseOtnMessage },
    { RULE_OPT__PRIORITY,         1, 1, ParseOtnPriority },
    { RULE_OPT__REFERENCE,        1, 0, ParseOtnReference },
    { RULE_OPT__REVISION,         1, 1, ParseOtnRevision },
    { RULE_OPT__SID,              1, 1, ParseOtnSid },
    { RULE_OPT__TAG,              1, 1, ParseOtnTag },
    { RULE_OPT__THRESHOLD,        1, 1, ParseOtnThreshold },

    { NULL, 0, 0, NULL }   /* Marks end of array */
};

/* Rule options
 * Only the basic ones are here.  The detection options and preprocessor
 * detection options define their own */
#define RULE_OPT__ACTIVATED_BY      "activated_by"
#define RULE_OPT__ACTIVATES         "activates"
#define RULE_OPT__CLASSTYPE         "classtype"
#define RULE_OPT__COUNT             "count"
#define RULE_OPT__DETECTION_FILTER  "detection_filter"
#define RULE_OPT__GID               "gid"
#define RULE_OPT__MSG               "msg"
#define RULE_OPT__METADATA          "metadata"
#define RULE_OPT__LOGTO             "logto"
#define RULE_OPT__PRIORITY          "priority"
#define RULE_OPT__REFERENCE         "reference"
#define RULE_OPT__REVISION          "rev"
#define RULE_OPT__SID               "sid"
#define RULE_OPT__TAG               "tag"
#define RULE_OPT__THRESHOLD         "threshold"


/***************************************************************/

typedef struct {
     unsigned gid;
     unsigned sid;
}rule_number_t;

typedef struct {
    int  max_rules;
    int  num_rules;
    rule_number_t * map;
}rule_index_map_t;

/****************************************************************************
 *
 * Function: ParseRuleOptions(char *, int)
 *
 * Purpose:  Process an individual rule's options and add it to the
 *           appropriate rule chain
 *
 * Arguments: rule => rule string
 *            rule_type => enumerated rule type (alert, pass, log)
 *            *conflicts => Identifies whether there was a conflict due to duplicate
 *                rule and whether existing otn was newer or not.
 *                0 - no conflict
 *                1 - existing otn is newer.
 *                -1 - existing otn is older.
 *
 * Returns:
 *  OptTreeNode *
 *      The new OptTreeNode on success or NULL on error.
 *
 ***************************************************************************/
OptTreeNode * ParseRuleOptions(SnortConfig *sc, RuleTreeNode *rtn,
                               char *rule_opts, RuleType rule_type, int protocol)
{
    OptTreeNode *otn;
    RuleOptOtnHandler otn_handler = NULL;
    int num_detection_opts = 0;
    char *dopt_keyword = NULL;
    OptFpList *fpl = NULL;
    int got_sid = 0;
    /**申請一個規則選項對象*/
    otn = (OptTreeNode *)SnortAlloc(sizeof(OptTreeNode));

    /**爲該規則選項設置一些基礎屬性*/
    otn->chain_node_number = otn_count;
    otn->proto = protocol;
    otn->event_data.sig_generator = GENERATOR_SNORT_ENGINE;
    otn->sigInfo.generator        = GENERATOR_SNORT_ENGINE;
    otn->sigInfo.rule_type        = SI_RULE_TYPE_DETECT; /* standard rule */
    otn->sigInfo.rule_flushing    = SI_RULE_FLUSHING_ON; /* usually just standard rules cause a flush*/

    /* Set the default rule state */
    /**設置默認狀態*/
    otn->rule_state = ScDefaultRuleState();
    /**若是該規則沒有規則選項*/
    if (rule_opts == NULL)
    {
        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "No rule options.\n"););
        /**沒有規則選項時但snort又須要規則選項中的sid時報錯*/
        if (ScRequireRuleSid())
            ParseError("Each rule must contain a Rule-sid.");
        /**實際是規則選項保留指向規則頭的指針,這樣就構成了整個規則對象*/
        addRtnToOtn(otn, getParserPolicy(sc), rtn);
        /**將該規則的gid 和sid 構成一個id對象存放在ruleIndexMap的一個數組中,而存放位置下標做爲返回值*/
        otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap,
                                         otn->sigInfo.generator,
                                         otn->sigInfo.id);
    }
    else    /**有規則選項,解釋規則選項*/
    {
        char **toks;
        int num_toks;
        /**字符數組大小爲解析規則選項的預製回調函數個數*/
        char configured[sizeof(rule_options) / sizeof(RuleOptFunc)];
        int i;
        OptTreeNode *otn_dup;
        /**檢查格式*/
        if ((rule_opts[0] != '(') || (rule_opts[strlen(rule_opts) - 1] != ')'))
        {
            ParseError("Rule options must be enclosed in '(' and ')'.");
        }

        /* Move past '(' and zero out ')' */
        /**去掉首尾括號*/
        rule_opts++;
        rule_opts[strlen(rule_opts) - 1] = '\0';

        /* Used to determine if a rule option has already been configured
         * in the rule.  Some can only be configured once */
        memset(configured, 0, sizeof(configured));
        /**提取規則選項條目*/
        toks = mSplit(rule_opts, ";", 0, &num_toks, '\\');

        for (i = 0; i < num_toks; i++)
        {
            char **opts;
            int num_opts;
            char *option_args = NULL;
            int j;

            DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"   option: %s\n", toks[i]););
            /**提取鍵值*/
            /* break out the option name from its data */
            opts = mSplit(toks[i], ":", 2, &num_opts, '\\');

            DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"   option name: %s\n", opts[0]););

            if (num_opts == 2)
            {
                option_args = opts[1];
                DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"   option args: %s\n", option_args););
            }

            for (j = 0; rule_options[j].name != NULL; j++)
            {
                /**檢查該鍵對應的RuleOptFunc是哪個*/
                if (strcasecmp(opts[0], rule_options[j].name) == 0)
                {
                    /**有的鍵值對應的數據只能獲取一次,若是屢次發現該鍵則報錯*/
                    if (configured[j] && rule_options[j].only_once)
                    {
                        ParseError("Only one '%s' rule option per rule.",
                                   opts[0]);
                    }
                    /**若果鍵沒有對應的值且該鍵須要參數則錯誤*/
                    if ((option_args == NULL) && rule_options[j].args_required)
                    {
                        ParseError("No argument passed to keyword \"%s\".  "
                                   "Make sure you didn't forget a ':' or the "
                                   "argument to this keyword.\n", opts[0]);
                    }
                    /**調用鍵對應的回調函數進行處理*/
                    rule_options[j].parse_func(sc, rtn, otn, rule_type, option_args);
                    /**標記該鍵已獲取過一次*/
                    configured[j] = 1;
                    break;
                }
            }

            /* Because we actually allow an sid of 0 */
            if ((rule_options[j].name != NULL) &&
                (strcasecmp(rule_options[j].name, RULE_OPT__SID) == 0))
            {
                got_sid = 1;
            }
            /**解析規則選項的回調函數中,部分必要的基礎數據解析使用的是
             * rule_options中的單元,但更多的輔助單元是以插件方式註冊的,
             * 例如 SetupFlowBits等鍵值對解析。
             *
             **/

             /**先檢查是不是探測類型的插件*/
            /* It's possibly a detection option plugin */
            if (rule_options[j].name == NULL)
            {
                RuleOptConfigFuncNode *dopt = rule_opt_config_funcs;
                /**遍歷全部規則選項解析插件*/
                for (; dopt != NULL; dopt = dopt->next)
                {
                    /**查找鍵值匹配的插件,如SetupTcpSeqCheck等*/
                    if (strcasecmp(opts[0], dopt->keyword) == 0)
                    {
                        /**調用該插件解析該單元*/
                        dopt->func(sc, option_args, otn, protocol);

                        /* If this option contains an OTN handler, save it for
                           use after the rule is done parsing. */
                        if (dopt->otn_handler != NULL)
                            otn_handler = dopt->otn_handler;

                        /* This is done so if we have a preprocessor/decoder
                         * rule, we can tell the user that detection options
                         * are not supported with those types of rules, and
                         * what the detection option is */
                         /**若是該插件試探查類插件將名字備份*/
                        if ((dopt_keyword == NULL) &&
                            (dopt->type == OPT_TYPE_DETECTION))
                        {
                            dopt_keyword = SnortStrdup(opts[0]);
                        }

                        break;
                    }
                }
                /**若是不是探測類型的插件,再來檢測他是不是預處理的插件*/
                if (dopt == NULL)
                {
                    /* Maybe it's a preprocessor rule option */
                    PreprocOptionInit initFunc = NULL;
                    PreprocOptionEval evalFunc = NULL;
                    PreprocOptionFastPatternFunc fpFunc = NULL;
                    PreprocOptionOtnHandler preprocOtnHandler = NULL;
                    PreprocOptionCleanup cleanupFunc = NULL;
                    void *opt_data = NULL;

                    /**獲取該關鍵字對應的預處理插件*/
                    int ret = GetPreprocessorRuleOptionFuncs
                        (sc, opts[0], &initFunc, &evalFunc,
                         &preprocOtnHandler, &fpFunc, &cleanupFunc);

                    if (ret && (initFunc != NULL))
                    {
                        /**初始化該插件*/
                        initFunc(sc, opts[0], option_args, &opt_data);
                        /**將該預處理處理報文的部分加入該規則選項實體持有的回調鏈中*/
                        AddPreprocessorRuleOption(sc, opts[0], otn, opt_data, evalFunc);
                        if (preprocOtnHandler != NULL)
                            otn_handler = (RuleOptOtnHandler)preprocOtnHandler;

                        DEBUG_WRAP(DebugMessage(DEBUG_INIT, "%s->", opts[0]););
                    }
                    else
                    {
                        /* Unrecognized rule option */
                        ParseError("Unknown rule option: '%s'.", opts[0]);
                    }
                }

                if (dopt_keyword == NULL)
                    dopt_keyword = SnortStrdup(opts[0]);

                num_detection_opts++;
            }

            mSplitFree(&opts, num_opts);
        }

        /**這一部分代碼是對自檢和藹後處理*/
        if ((dopt_keyword != NULL) &&
            (otn->sigInfo.rule_type != SI_RULE_TYPE_DETECT))
        {
            /* Preprocessor and decoder rules can not have
             * detection options */
            ParseError("Preprocessor and decoder rules do not support "
                       "detection options: %s.", dopt_keyword);
        }

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

        if (!got_sid && !ScTestMode())
            ParseError("Each rule must contain a rule sid.");

        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"OptListEnd\n"););
        /** 創建起規則選項實體到規則頭部實體的映射*/
        addRtnToOtn(otn, getParserPolicy(sc), rtn);

        /* Check for duplicate SID */
        /** 檢查sid 衝突*/
        otn_dup = OtnLookup(sc->otn_map, otn->sigInfo.generator, otn->sigInfo.id);
        if (otn_dup != NULL)
        {
            otn->ruleIndex = otn_dup->ruleIndex;

            if (mergeDuplicateOtn(sc, otn_dup, otn, rtn) == 0)
            {
                /* We are keeping the old/dup OTN and trashing the new one
                 * we just created - it's free'd in the remove dup function */
                mSplitFree(&toks, num_toks);
                return NULL;
            }
        }
        else
        {
/** 保存規則的gid 和 sid, 返回兩個數據構成的對象在存放該對象集合的數組中的位置*/
            otn->ruleIndex = RuleIndexMapAdd(ruleIndexMap,
                                             otn->sigInfo.generator,
                                             otn->sigInfo.id);
        }

        mSplitFree(&toks, num_toks);
    }
    /**更新該規則選項所擁有的探測計數*/
    otn->num_detection_opts += num_detection_opts;
    /**更新總的規則選項計數*/
    otn_count++;
    /**若是該規則選項是探測型的,則增長探測規則計數*/
    if (otn->sigInfo.rule_type == SI_RULE_TYPE_DETECT)
    {
        detect_rule_count++;
    }
    else if (otn->sigInfo.rule_type == SI_RULE_TYPE_DECODE)
    {
    /**若是是設置decode的則反轉指定sid的規則decode標識是否使能*/
        //Set the bit if the decoder rule is enabled in the policies
        UpdateDecodeRulesArray(otn->sigInfo.id, ENABLE_RULE, ENABLE_ONE_RULE);
        decode_rule_count++;
    }
    /**若是該規則選項是預處理類型的則增長預處理計數*/
    else if (otn->sigInfo.rule_type == SI_RULE_TYPE_PREPROC)
    {
        preproc_rule_count++;
    }
    /**向該選項節點的規則選項回調鏈表中追加一個尾部標誌*/
    fpl = AddOptFuncToList(OptListEnd, otn);
    fpl->type = RULE_OPTION_TYPE_LEAF_NODE;

    if (otn_handler != NULL)
    {
        otn_handler(sc, otn);
    }
    /**這兩個主要是針對字串的模式匹配,後續專門分析*/
    FinalizeContentUniqueness(sc, otn);
    ValidateFastPattern(otn);

    if ((thdx_tmp != NULL) && (otn->detection_filter != NULL))
    {
        ParseError("The \"detection_filter\" rule option and the \"threshold\" "
                   "rule option cannot be used in the same rule.\n");
    }
    /**啓用閥值時的一些檢測不用太關注*/
    if (thdx_tmp != NULL)
    {
        int rstat;

        thdx_tmp->sig_id = otn->sigInfo.id;
        thdx_tmp->gen_id = otn->sigInfo.generator;
        rstat = sfthreshold_create(sc, sc->threshold_config, thdx_tmp);

        if (rstat)
        {
            if (rstat == THD_TOO_MANY_THDOBJ)
            {
                ParseError("threshold (in rule): could not create threshold - "
                           "only one per sig_id=%u.", thdx_tmp->sig_id);
            }
            else
            {
                ParseError("threshold (in rule): could not add threshold "
                           "for sig_id=%u!\n", thdx_tmp->sig_id);
            }
        }

        thdx_tmp = NULL;
    }

    /* setup gid,sid->otn mapping */
    /**下面的兩個函數都是將以otn中的sid 和 gid做爲鍵將otn加入sfghash
     * 不一樣的是當發現其中有鍵徹底相同的數據時的處理
     * 1.第一個函數會將該otn連接在sfgash查到的重複otn的一個專有鏈表中
     * 2.第二個則會直接報錯
     */
    SoRuleOtnLookupAdd(sc->so_rule_otn_map, otn);
    OtnLookupAdd(sc->otn_map, otn);

    return otn;
}

/**根據關鍵字獲取預處理插件*/
int GetPreprocessorRuleOptionFuncs(
    SnortConfig *sc,
    char *optionName,
    PreprocOptionInit* initFunc,
    PreprocOptionEval* evalFunc,
    PreprocOptionOtnHandler* otnHandler,
    PreprocOptionFastPatternFunc* fpFunc,
    PreprocOptionCleanup* cleanupFunc
    )
{
    /**由於預處理插件會註冊多個函數,因此使用PreprocessorOptionInfo管理一個預處理插件的全部回調接口*/
    PreprocessorOptionInfo *optionInfo;
    SnortPolicy *p;

    if (sc == NULL)
    {
        FatalError("%s(%d) Snort conf for parsing is NULL.\n",
                   __FILE__, __LINE__);
    }

    p = sc->targeted_policies[getParserPolicy(sc)];
    if (p == NULL)
        return 0;

    if (p->preproc_rule_options == NULL)
    {
        FatalError("Preprocessor Rule Option storage not initialized\n");
    }

    /**爲了快速查找使用sfhash管理PreprocessorOptionInfo,這裏是從sfghash中獲取預處理插件*/
    optionInfo = sfghash_find(p->preproc_rule_options, optionName);
    if (!optionInfo)
    {
        return 0;
    }
    /**從找到的PreprocessorOptionInfo中提取該預處理插件的回調函數*/
    *initFunc = (PreprocOptionInit)optionInfo->optionInit;  /**該回調主要是作初始化工做*/
    *evalFunc = (PreprocOptionEval)optionInfo->optionEval;  /**返回匹配等處理後得到的狀態標誌*/
    *fpFunc = (PreprocOptionFastPatternFunc)optionInfo->optionFpFunc;
    *otnHandler = (PreprocOptionOtnHandler)optionInfo->otnHandler;
    *cleanupFunc = (PreprocOptionCleanup)optionInfo->optionCleanup;

    return 1;
}
/***********************************************************************************************************************/

/* same as the rule header FP list */
typedef struct _OptFpList
{
    /* context data for this test */
    void *context;

    int (*OptTestFunc)(void *option_data, Packet *p);

    struct _OptFpList *next;

    unsigned char isRelative;
    option_type_t type;

} OptFpList;


int AddPreprocessorRuleOption(SnortConfig *sc, char *optionName, OptTreeNode *otn, void *data, PreprocOptionEval evalFunc)
{
    OptFpList *fpl;
    PreprocessorOptionInfo *optionInfo;
    PreprocessorOptionInfo *saveOptionInfo;
    /**這是用來獲取數據的指針*/
    void *option_dup;
    SnortPolicy *p;

    if (sc == NULL)
    {
        FatalError("%s(%d) Snort conf for parsing is NULL.\n",
                   __FILE__, __LINE__);
    }

    p = sc->targeted_policies[getParserPolicy(sc)];
    if (p == NULL)
        return 0;
    /**這裏再次利用關鍵字獲取到該預處理插件
     *
     */
    optionInfo = sfghash_find(p->preproc_rule_options, optionName);

    if (!optionInfo)
        return 0;

    saveOptionInfo = (PreprocessorOptionInfo *)SnortAlloc(sizeof(PreprocessorOptionInfo));

    memcpy(saveOptionInfo, optionInfo, sizeof(PreprocessorOptionInfo));
    /**注意, 在初始化該插件是容許返回一個數據,這裏將該數據使用PreprocessorOptionInfo保存*/
    saveOptionInfo->data = data;

    /**將該插件的功能函數(該函數會解析報文數據)加入該規則選項的opt_func鏈表中*/
    //  Add to option chain with generic callback
    fpl = AddOptFuncToList(PreprocessorOptionFunc, otn);

    /*
     * attach custom info to the context node so that we can call each instance
     * individually
     */
     /**將fp1關聯生成他的預處理器*/
    fpl->context = (void *) saveOptionInfo;
    /**這要是將初始化該插件時返回的數據保存在hash表中*/
    if (add_detection_option(sc, RULE_OPTION_TYPE_PREPROCESSOR,
                             (void *)saveOptionInfo, &option_dup) == DETECTION_OPTION_EQUAL)
    {
        PreprocessorRuleOptionsFreeFunc(saveOptionInfo);
        fpl->context = saveOptionInfo = option_dup;
    }
    fpl->type = RULE_OPTION_TYPE_PREPROCESSOR;

    return 1;
}

/********************************************************************************/
/****************************************************************************
 *
 * Function: AddOptFuncToList(int (*func)(), OptTreeNode *)
 *
 * Purpose: Links the option detection module to the OTN
 *
 * Arguments: (*func)() => function pointer to the detection module
 *            otn =>  pointer to the current OptTreeNode
 *
 * Returns: void function
 *
 ***************************************************************************/
OptFpList * AddOptFuncToList(RuleOptEvalFunc ro_eval_func, OptTreeNode *otn)
{
    OptFpList *ofp = (OptFpList *)SnortAlloc(sizeof(OptFpList));

    DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Adding new rule to list\n"););
    /**將插件的回調函數加入該規則選項的opt_func鏈表中*/
    /* if there are no nodes on the function list... */
    if (otn->opt_func == NULL)
    {
        otn->opt_func = ofp;
    }
    else
    {
        OptFpList *tmp = otn->opt_func;

        /* walk to the end of the list */
        while (tmp->next != NULL)
            tmp = tmp->next;

        tmp->next = ofp;
    }

    DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Set OptTestFunc to %p\n", ro_eval_func););
    /**指向最後一個被加入的回調函數**/
    ofp->OptTestFunc = ro_eval_func;

    return ofp;
}

/****************************************************************************/
/**Add RTN to OTN for a particular OTN.
 * @param otn pointer to structure OptTreeNode.
 * @param policyId policy id
 * @param rtn pointer to RuleTreeNode structure
 *
 * @return 0 if successful,
 *         -ve otherwise
 */
int addRtnToOtn(
        OptTreeNode *otn,
        tSfPolicyId policyId,
        RuleTreeNode *rtn
        )
{
    if (otn->proto_node_num <= policyId)
    {
        /* realloc the list, initialize missing elements to 0 and add
         * policyId */
        RuleTreeNode **tmpNodeArray;
        unsigned int numNodes = (policyId + 1);

        tmpNodeArray = SnortAlloc(sizeof(RuleTreeNode *) * numNodes);

        /* copy original contents, the remaining elements are already
         * zeroed out by snortAlloc */
        if (otn->proto_nodes)
        {
            memcpy(tmpNodeArray, otn->proto_nodes,
                sizeof(RuleTreeNode *) * otn->proto_node_num);
            free(otn->proto_nodes);
        }

        otn->proto_node_num = numNodes;
        otn->proto_nodes = tmpNodeArray;
    }

    //add policyId
    if (otn->proto_nodes[policyId])
    {
        DestroyRuleTreeNode(rtn);
        return -1;
    }
    /**實際就這裏最關鍵,這裏創建起規則選項實體到規則頭部實體的映射*/
    otn->proto_nodes[policyId] = rtn;

    return 0; //success
}


總結

結合前面的分析總結出snort在規則的解析和插件的管理上有以下幾點數組

  1. 各種插件的管理相分離app

  2. 規則被拆分爲 規則頭 規則選項, 一個規則頭能夠被多個規則選項使用。這是亮點,不只井井有條並且不論從空間和時間上都較看爲一個總體處理開銷更小框架

  3. 插件在規則解析中觸發的核心思想就一句話「處理方式同樣的簡單屬性我就預約義了,其餘的屬性交給用戶了,還不能解析的屬性就完蛋了」函數

相關文章
相關標籤/搜索