简介
snort中较重要的一个环节就是配置文件的读取。以snort-2.9.6.0为示例
该过程完成以下几件事
确定被加载的模块,并能为部分模块获得需要的配置参数
获取构建匹配结构需要的数据
规则文件范例
内容较多,可以下载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 bugs@snort.org 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;
}
总结
snort规则支持换行符
不仅'#',使用 ';'作为行头任然能达到注释该行的效果
以动作来分类系统预定义的规则优先于用户自定义的规则
来源:oschina
链接:https://my.oschina.net/u/572632/blog/289256