snort读取规则选项

***************************************************************/
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
}
posted @ 2015-03-29 13:27  孙永杰  阅读(1423)  评论(0编辑  收藏  举报