***************************************************************/
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
}