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 [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; }
snort规则支持换行符
不仅'#',使用 ';'作为行头任然能达到注释该行的效果
以动作来分类系统预定义的规则优先于用户自定义的规则