snort 中的规则解析框架(一)

简介

        snort中较重要的一个环节就是配置文件的读取。以snort-2.9.6.0为示例

        该过程完成以下几件事

  1. 确定被加载的模块,并能为部分模块获得需要的配置参数

  2. 获取构建匹配结构需要的数据

规则文件范例

        内容较多,可以下载snort 配置手册

代码分析

     以下代码是从几个部分抽取关键片段合并而成

 typedef struct _KeywordFunc
{
    char *name;             /**关键字名,用于匹配*/
    KeywordType type;       /**内型,是个枚举值*/
    int expand_vars;        /**是否要扩展*/
    int default_policy_only;/**是否只能默认配置策略*/
    ParseFunc parse_func;   /**解析该关键字的回调函数*/
} KeywordFunc;


static const KeywordFunc snort_conf_keywords[] =
{
    /* Rule keywords */
    { SNORT_CONF_KEYWORD__ACTIVATE, KEYWORD_TYPE__RULE, 0, 0, ParseActivate },
    { SNORT_CONF_KEYWORD__ALERT,    KEYWORD_TYPE__RULE, 0, 0, ParseAlert },
    { SNORT_CONF_KEYWORD__DROP,     KEYWORD_TYPE__RULE, 0, 0, ParseDrop },
    { SNORT_CONF_KEYWORD__BLOCK,    KEYWORD_TYPE__RULE, 0, 0, ParseDrop },
    { SNORT_CONF_KEYWORD__DYNAMIC,  KEYWORD_TYPE__RULE, 0, 0, ParseDynamic },
    { SNORT_CONF_KEYWORD__LOG,      KEYWORD_TYPE__RULE, 0, 0, ParseLog },
    { SNORT_CONF_KEYWORD__PASS,     KEYWORD_TYPE__RULE, 0, 0, ParsePass },
    { SNORT_CONF_KEYWORD__REJECT,   KEYWORD_TYPE__RULE, 0, 0, ParseReject },
    { SNORT_CONF_KEYWORD__SDROP,    KEYWORD_TYPE__RULE, 0, 0, ParseSdrop },
    { SNORT_CONF_KEYWORD__SBLOCK,   KEYWORD_TYPE__RULE, 0, 0, ParseSdrop },
    ..../** 后面有更多条目*/
};

static void ParseConfigFile(SnortConfig *sc, SnortPolicy *p, char *fname)
{
    /* Used for line continuation */
    int continuation = 0;
    char *saved_line = NULL;
    char *new_line = NULL;
    char *buf = (char *)SnortAlloc(MAX_LINE_LENGTH + 1);
    FILE *fp = fopen(fname, "r");							/** 打开文件*/

    /* open the rules file */
    if (fp == NULL)											/** 打开失败输出信息*/
    {
        ParseError("Unable to open rules file \"%s\": %s.\n",
                   fname, strerror(errno));
    }

    /* loop thru each file line and send it to the rule parser */
    while ((fgets(buf, MAX_LINE_LENGTH, fp)) != NULL)	   /* 一次分析一行*/
    {
        /* buffer indexing pointer */
        char *index = buf;

        /* Increment the line counter so the error messages know which
         * line to bitch about */
        file_line++;										
        /** 增加行计数*/

        /* fgets always appends a null, so doing a strlen should be safe */
        if ((strlen(buf) + 1) == MAX_LINE_LENGTH)          /** 检查读入的数据大小*/
        {
            ParseError("Line greater than or equal to %u characters which is "
                       "more than the parser is willing to handle.  Try "
                       "splitting it up on multiple lines if possible.",
                       MAX_LINE_LENGTH);
        }

        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Got line %s (%d): %s\n",
                                fname, file_line, buf););

        /* advance through any whitespace at the beginning of the line */
         /** 去掉头部的空格*/
        while (isspace((int)*index))                     
            index++;

        /* If it's an empty line or starts with a comment character */
        /** ’#‘ ,‘,’ 开头以及空串都将跳过*/
        if ((strlen(index) == 0) || (*index == '#') || (*index == ';'))
            continue;
        /**有换行标志,标识前面还有部分应该也属于该行*/
        if (continuation)                               
        {
            int new_line_len = strlen(saved_line) + strlen(index) + 1;
            /**长度校验*/
            if (new_line_len >= PARSERULE_SIZE)         
            {
                ParseError("Rule greater than or equal to %u characters which "
                           "is more than the parser is willing to handle.  "
                           "Submit a bug to [email protected] if you legitimately "
                           "feel like your rule or keyword configuration needs "
                           "more than this amount of space.", PARSERULE_SIZE);
            }
                                                        /**两行合并*/
            new_line = (char *)SnortAlloc(new_line_len);
            snprintf(new_line, new_line_len, "%s%s", saved_line, index);

            free(saved_line);
            saved_line = NULL;
            index = new_line;

            DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"concat rule: %s\n",
                                    new_line););
        }

        /* check for a '\' continuation character at the end of the line
         * if it's there we need to get the next line in the file */
        if (ContinuationCheck(index) == 0)              /**检查是否有换行符*/
        {
            char **toks;
            int num_toks;
            char *keyword;
            char *args;
            int i;

            DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,
                                    "[*] Processing keyword: %s\n", index););

            /* Get the keyword and args */

            /*提取关键字和参数*/
            toks = mSplit(index, " \t", 2, &num_toks, 0);
            if (num_toks != 2)
                ParseError("Invalid configuration line: %s", index);

            keyword = SnortStrdup(ExpandVars(sc, toks[0]));
            args = toks[1];

            for (i = 0; snort_conf_keywords[i].name != NULL; i++)
            {
                /**遍历所有可用的关键字,找到匹配的关键字条目*/
                if (strcasecmp(keyword, snort_conf_keywords[i].name) == 0)
                {
                    /**只能默认配置的条目不处理*/
                    if ((getParserPolicy(sc) != getDefaultPolicy()) &&
                        snort_conf_keywords[i].default_policy_only)
                    {
                        /* Keyword only configurable in the default policy*/
                        DEBUG_WRAP(DebugMessage(DEBUG_INIT,
                            "Config option \"%s\" configurable only by default policy. Ignoring it", toks[0]));
                        break;
                    }

                    if (((snort_conf_keywords[i].type == KEYWORD_TYPE__RULE) &&
                         !parse_rules) ||
                        ((snort_conf_keywords[i].type == KEYWORD_TYPE__MAIN) &&
                         parse_rules))
                    {
                        break;
                    }

                    /** 该条目需要进行扩展,先扩展内容, TODO 后续关注*/
                    if (snort_conf_keywords[i].expand_vars)
                        args = SnortStrdup(ExpandVars(sc, toks[1]));

                    /* Special parsing case is ruletype.
                     * Need to send the file pointer so it can parse what's
                     * between '{' and '}' which can span multiple lines
                     * without a line continuation character */
                     /**rule type 比较特殊是用来自定义动作的, 参见snort配置手册*/
                    if (strcasecmp(keyword, SNORT_CONF_KEYWORD__RULE_TYPE) == 0)
                        _ParseRuleTypeDeclaration(sc, fp, args, parse_rules);
                    else
                        snort_conf_keywords[i].parse_func(sc, p, args);
                        /**调用匹配的回调函数处理规则*/
                    break;
                }
            }

            /* Didn't find any pre-defined snort_conf_keywords.  Look for a user defined
             * rule type */
            /** 检查是否预定义的关键字全部未命中,使用用户定义的关键字,
              * 用户定义的关键字来自上面的ruleType
              */
            if ((snort_conf_keywords[i].name == NULL) && parse_rules)
            {
                RuleListNode *node;

                DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Unknown rule type, "
                                        "might be declared\n"););

                for (node = sc->rule_lists; node != NULL; node = node->next)
                {
                    if (strcasecmp(node->name, keyword) == 0)
                        break;
                }

                if (node == NULL)
                    ParseError("Unknown rule type: %s.", toks[0]);

                if ( node->mode == RULE_TYPE__DROP )
                {
                    if ( ScTreatDropAsAlert() )
                        ParseRule(sc, p, args, RULE_TYPE__ALERT, node->RuleList);

                    else if ( ScKeepDropRules() ||  ScLoadAsDropRules() )
                        ParseRule(sc, p, args, node->mode, node->RuleList);
                }
                else if ( node->mode == RULE_TYPE__SDROP )
                {
                    if ( ScKeepDropRules() && !ScTreatDropAsAlert() )
                        ParseRule(sc, p, args, node->mode, node->RuleList);

                    else if ( ScLoadAsDropRules() )
                        ParseRule(sc, p, args, RULE_TYPE__DROP, node->RuleList);
                }
                else
                {
                    ParseRule(sc, p, args, node->mode, node->RuleList);
                }
            }

            if (args != toks[1])
                free(args);

            free(keyword);
            mSplitFree(&toks, num_toks);

            if(new_line != NULL)
            {
                free(new_line);
                new_line = NULL;
                continuation = 0;
            }
        }
        else                                    /** 遇上换行符*/
        {
            /* save the current line */
            saved_line = SnortStrdup(index);    /**保持该行,获得一个副本*/

            /* current line was a continuation itself... */
            if (new_line != NULL)               /**释放读取的行*/
            {
                free(new_line);
                new_line = NULL;
            }

            /* set the flag to let us know the next line is
             * a continuation line */
            continuation = 1;                   /**标记换行标志*/
        }
    }

    fclose(fp);                                 /**关闭文件*/
    free(buf);                                  /**释放缓冲区*/
}

/** 换行符的检查以及处理*/
static int ContinuationCheck(char *rule)
{
    char *idx;  /* indexing var for moving around on the string */

    idx = rule + strlen(rule) - 1;     /**指向串尾*/

    DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"initial idx set to \'%c\'\n",
                *idx););

    while(isspace((int)*idx))          /** 去掉尾部的空格*/
    {
        idx--;
    }

    if(*idx == '\\')                    /** 遇到换行符, snort规则支持换行符*/
    {
        DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Got continuation char, "
                    "clearing char and returning 1\n"););

        /* clear the '\' so there isn't a problem on the appended string */
        *idx = '\x0';                   /** 清除换行, 替换为空字符*/
        return 1;
    }

    return 0;
}

总结

  1. snort规则支持换行符

  2. 不仅'#',使用 ';'作为行头任然能达到注释该行的效果

  3. 以动作来分类系统预定义的规则优先于用户自定义的规则

   


    

你可能感兴趣的:(snort)