入侵检测系统首推开源项目snort,完善的匹配规则定义,灵活的插件扩充能力使其有强大的生命力,创建规则匹配的核心代码节选如下:
可以看出,在分解规则的时候,就已经用多个链表,将不同的规则进行分类了,可以根据分析的场景,有选择的去使用合适的规则,实现高效。
void ParseRule(char *prule, int inclevel)
{
char **toks; /* dbl ptr for mSplit call, holds rule tokens */
int num_toks; /* holds number of tokens found by mSplit */
int rule_type; /* rule type enumeration variable */
char rule[1024];
int protocol;
RuleTreeNode proto_node;
/* clean house */
bzero((char *) &proto_node, sizeof(RuleTreeNode));
/* chop off the <CR/LF> from the string */
strip(prule);
/* expand all variables */
strcpy(rule, ExpandVars(prule));
/* break out the tokens from the rule string */
toks = mSplit(rule, " ", 10, &num_toks, 0);
#ifdef DEBUG
printf("[*] Rule start\n");
#endif
/* figure out what we're looking at */
rule_type = RuleType(toks[0]);
#ifdef DEBUG
printf("Rule type: ");
#endif
/* handle non-rule entries */
switch (rule_type)
{
case RULE_PASS:
#ifdef DEBUG
printf("Pass\n");
#endif
break;
case RULE_LOG:
#ifdef DEBUG
printf("Log\n");
#endif
break;
case RULE_ALERT:
#ifdef DEBUG
printf("Alert\n");
#endif
break;
case RULE_INCLUDE:
#ifdef DEBUG
printf("Include\n");
#endif
ParseRulesFile(toks[1], inclevel + 1);
return;
case RULE_VAR:
#ifdef DEBUG
printf("Variable\n");
#endif
VarDefine(toks[1], toks[2]);
return;
case RULE_PREPROCESS:
#ifdef DEBUG
printf("Preprocessor\n");
#endif
ParsePreprocessor(rule);
return;
case RULE_OUTPUT:
#ifdef DEBUG
printf("Output Plugin\n");
#endif
ParseOutputPlugin(rule);
return;
case RULE_ACTIVATE:
#ifdef DEBUG
printf("Activation rule\n");
#endif
break;
case RULE_DYNAMIC:
#ifdef DEBUG
printf("Dynamic rule\n");
#endif
break;
case RULE_CONFIG:
#ifdef DEBUG
printf("Rule file config\n");
#endif
ParseConfig(rule);
return;
case RULE_DECLARE:
#ifdef DEBUG
printf("Rule type declaration\n");
#endif
ParseRuleTypeDeclaration(rule);
return;
case RULE_UNKNOWN:
#ifdef DEBUG
printf("Unknown rule type, might be declared\n");
#endif
ParseDeclaredRuleType(rule);
return;
default:
printf("Invalid input: %s\n", prule);
return;
}
proto_node.type = rule_type;
/* set the rule protocol */
protocol = WhichProto(toks[1]);
/* Process the IP address and CIDR netmask */
/* changed version 1.2.1 */
/*
* "any" IP's are now set to addr 0, netmask 0, and the normal rules are
* applied instead of checking the flag
*/
/*
* if we see a "!<ip number>" we need to set a flag so that we can
* properly deal with it when we are processing packets
*/
if (*toks[2] == '!') /* we found a negated address */
{
proto_node.flags |= EXCEPT_SRC_IP;
ParseIP(&toks[2][1], (u_long *) & proto_node.sip,
(u_long *) & proto_node.smask);
} else
{
ParseIP(toks[2], (u_long *) & proto_node.sip,
(u_long *) & proto_node.smask);
}
/* do the same for the port */
if (ParsePort(toks[3], (u_short *) & proto_node.hsp,
(u_short *) & proto_node.lsp, toks[1],
(int *) &proto_node.not_sp_flag))
{
proto_node.flags |= ANY_SRC_PORT;
}
if (proto_node.not_sp_flag)
proto_node.flags |= EXCEPT_SRC_PORT;
/* New in version 1.3: support for bidirectional rules */
/*
* this checks the rule "direction" token and sets the bidirectional flag
* if the token = '<>'
*/
if (!strncmp("<>", toks[4], 2))
{
#ifdef DEBUG
printf("Bidirectional rule!\n");
#endif
proto_node.flags |= BIDIRECTIONAL;
}
/* changed version 1.2.1 */
/*
* "any" IP's are now set to addr 0, netmask 0, and the normal rules are
* applied instead of checking the flag
*/
/*
* if we see a "!<ip number>" we need to set a flag so that we can
* properly deal with it when we are processing packets
*/
if (*toks[5] == '!') /* we found a negated address */
{
#ifdef DEBUG
printf("setting exception flag for dest IP\n");
#endif
proto_node.flags |= EXCEPT_DST_IP;
ParseIP(&toks[5][1], (u_long *) & proto_node.dip,
(u_long *) & proto_node.dmask);
} else
ParseIP(toks[5], (u_long *) & proto_node.dip,
(u_long *) & proto_node.dmask);
if (ParsePort(toks[6], (u_short *) & proto_node.hdp,
(u_short *) & proto_node.ldp, toks[1],
(int *) &proto_node.not_dp_flag))
{
proto_node.flags |= ANY_DST_PORT;
}
if (proto_node.not_dp_flag)
proto_node.flags |= EXCEPT_DST_PORT;
#ifdef DEBUG
printf("proto_node.flags = 0x%X\n", proto_node.flags);
printf("Processing Head Node....\n");
#endif
switch (rule_type)
{
case RULE_ALERT:
ProcessHeadNode(&proto_node, &Alert, protocol);
break;
case RULE_LOG:
ProcessHeadNode(&proto_node, &Log, protocol);
break;
case RULE_PASS:
ProcessHeadNode(&proto_node, &Pass, protocol);
break;
case RULE_ACTIVATE:
ProcessHeadNode(&proto_node, &Activation, protocol);
break;
case RULE_DYNAMIC:
ProcessHeadNode(&proto_node, &Dynamic, protocol);
break;
default:
FatalError("Unable to determine rule type (%s) for processing, exiting!\n", toks[0]);
}
rule_count++;
#ifdef DEBUG
printf("Parsing Rule Options...\n");
#endif
ParseRuleOptions(rule, rule_type, protocol);
free(toks);
return;
}
/****************************************************************************
*
* Function: ProcessHeadNode(RuleTreeNode *, ListHead *, int)
*
* Purpose: Process the header block info and add to the block list if
* necessary
*
* Arguments: test_node => data generated by the rules parsers
* list => List Block Header refernece
* protocol => ip protocol
*
* Returns: void function
*
***************************************************************************/
void ProcessHeadNode(RuleTreeNode * test_node, ListHead * list, int protocol)
{
int match = 0;
RuleTreeNode *rtn_idx;
int count = 0;
/* select the proper protocol list to attach the current rule to */
switch (protocol)
{
case IPPROTO_TCP:
rtn_idx = list->TcpList;
break;
case IPPROTO_UDP:
rtn_idx = list->UdpList;
break;
case IPPROTO_ICMP:
rtn_idx = list->IcmpList;
break;
default:
rtn_idx = NULL;
break;
}
/*
* if the list head is NULL (empty), make a new one and attach the
* ListHead to it
*/
if (rtn_idx == NULL)
{
head_count++;
switch (protocol)
{
case IPPROTO_TCP:
list->TcpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
rtn_tmp = list->TcpList;
break;
case IPPROTO_UDP:
list->UdpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
rtn_tmp = list->UdpList;
break;
case IPPROTO_ICMP:
list->IcmpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
rtn_tmp = list->IcmpList;
break;
}
/* copy the prototype header data into the new node */
XferHeader(test_node, rtn_tmp);
rtn_tmp->head_node_number = head_count;
/* null out the down (options) pointer */
rtn_tmp->down = NULL;
/* add the function list to the new rule */
SetupRTNFuncList(rtn_tmp);
/* add link to parent listhead */
rtn_tmp->listhead = list;
return;
}
/* see if this prototype node matches any of the existing header nodes */
match = TestHeader(rtn_idx, test_node);
while ((rtn_idx->right != NULL) && !match)
{
count++;
match = TestHeader(rtn_idx, test_node);
if (!match)
rtn_idx = rtn_idx->right;
else
break;
}
/*
* have to check this twice since my loop above exits early, which sucks
* but it's not performance critical
*/
match = TestHeader(rtn_idx, test_node);
/*
* if it doesn't match any of the existing nodes, make a new node and
* stick it at the end of the list
*/
if (!match)
{
#ifdef DEBUG
printf("Building New Chain head node\n");
#endif
head_count++;
/* build a new node */
rtn_idx->right = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
/* set the global ptr so we can play with this from anywhere */
rtn_tmp = rtn_idx->right;
/* uh oh */
if (rtn_tmp == NULL)
{
FatalError("ERROR: Unable to allocate Rule Head Node!!\n");
}
/* copy the prototype header info into the new header block */
XferHeader(test_node, rtn_tmp);
rtn_tmp->head_node_number = head_count;
rtn_tmp->down = NULL;
/* initialize the function list for the new RTN */
SetupRTNFuncList(rtn_tmp);
/* add link to parent listhead */
rtn_tmp->listhead = list;
#ifdef DEBUG
printf("New Chain head flags = 0x%X\n", rtn_tmp->flags);
#endif
} else
{
rtn_tmp = rtn_idx;
#ifdef DEBUG
printf("Chain head %d flags = 0x%X\n", count, rtn_tmp->flags);
#endif
#ifdef DEBUG
printf("Adding options to chain head %d\n", count);
#endif
}
}
/****************************************************************************
*
* Function: AddRuleFuncToList(int (*func)(), RuleTreeNode *)
*
* Purpose: Adds RuleTreeNode associated detection functions to the
* current rule's function list
*
* Arguments: *func => function pointer to the detection function
* rtn => pointer to the current rule
*
* Returns: void function
*
***************************************************************************/
void AddRuleFuncToList(int (*func) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *), RuleTreeNode * rtn)
{
RuleFpList *idx;
#ifdef DEBUG
printf("Adding new rule to list\n");
#endif
idx = rtn->rule_func;
if (idx == NULL)
{
rtn->rule_func = (RuleFpList *) calloc(sizeof(RuleFpList), sizeof(char));
rtn->rule_func->RuleHeadFunc = func;
} else
{
while (idx->next != NULL)
idx = idx->next;
idx->next = (RuleFpList *) calloc(sizeof(RuleFpList), sizeof(char));
idx = idx->next;
idx->RuleHeadFunc = func;
}
}
/****************************************************************************
*
* Function: SetupRTNFuncList(RuleTreeNode *)
*
* Purpose: Configures the function list for the rule header detection
* functions (addrs and ports)
*
* Arguments: rtn => the pointer to the current rules list entry to attach to
*
* Returns: void function
*
***************************************************************************/
void SetupRTNFuncList(RuleTreeNode * rtn)
{
#ifdef DEBUG
printf("Initializing RTN function list!\n");
printf("Functions: ");
#endif
if (rtn->flags & BIDIRECTIONAL)
{
#ifdef DEBUG
printf("CheckBidirectional->\n");
#endif
AddRuleFuncToList(CheckBidirectional, rtn);
} else
{
/* link in the proper IP address detection function */
/*
* the in-line "if" determines whether or not the negation operator
* has been set for this rule and tells the AddrToFunc call which
* function it should be linking in
*/
AddrToFunc(rtn, rtn->sip, rtn->smask, (rtn->flags & EXCEPT_SRC_IP ? 1 : 0), SRC);
/* last verse, same as the first (but for dest IP) ;) */
AddrToFunc(rtn, rtn->dip, rtn->dmask, (rtn->flags & EXCEPT_DST_IP ? 1 : 0), DST);
/* Attach the proper port checking function to the function list */
/*
* the in-line "if's" check to see if the "any" or "not" flags have
* been set so the PortToFunc call can determine which port testing
* function to attach to the list
*/
PortToFunc(rtn, (rtn->flags & ANY_SRC_PORT ? 1 : 0),
(rtn->flags & EXCEPT_SRC_PORT ? 1 : 0), SRC);
/* as above */
PortToFunc(rtn, (rtn->flags & ANY_DST_PORT ? 1 : 0),
(rtn->flags & EXCEPT_DST_PORT ? 1 : 0), DST);
}
#ifdef DEBUG
printf("RuleListEnd\n");
#endif
/* tack the end (success) function to the list */
AddRuleFuncToList(RuleListEnd, rtn);
}
/****************************************************************************
*
* Function: AddrToFunc(RuleTreeNode *, u_long, u_long, int, int)
*
* Purpose: Links the proper IP address testing function to the current RTN
* based on the address, netmask, and addr flags
*
* Arguments: rtn => the pointer to the current rules list entry to attach to
* ip => IP address of the current rule
* mask => netmask of the current rule
* exception_flag => indicates that a "!" has been set for this
* address
* mode => indicates whether this is a rule for the source
* or destination IP for the rule
*
* Returns: void function
*
***************************************************************************/
void AddrToFunc(RuleTreeNode * rtn, u_long ip, u_long mask, int exception_flag, int mode)
{
/*
* if IP and mask are both 0, this is a "any" IP and we don't need to
* check it
*/
if ((ip == 0) && (mask == 0))
return;
/* if the exception flag is up, test with the exception function */
if (exception_flag)
{
switch (mode)
{
case SRC:
#ifdef DEBUG
printf("CheckSrcIPNotEq -> ");
#endif
AddRuleFuncToList(CheckSrcIPNotEq, rtn);
break;
case DST:
#ifdef DEBUG
printf("CheckDstIPNotEq -> ");
#endif
AddRuleFuncToList(CheckDstIPNotEq, rtn);
break;
}
return;
}
switch (mode)
{
case SRC:
#ifdef DEBUG
printf("CheckSrcIPEqual -> ");
#endif
AddRuleFuncToList(CheckSrcIPEqual, rtn);
break;
case DST:
#ifdef DEBUG
printf("CheckDstIPEqual -> ");
#endif
AddRuleFuncToList(CheckDstIPEqual, rtn);
break;
}
}
/****************************************************************************
*
* Function: PortToFunc(RuleTreeNode *, int, int, int)
*
* Purpose: Links in the port analysis function for the current rule
*
* Arguments: rtn => the pointer to the current rules list entry to attach to
* any_flag => accept any port if set
* except_flag => indicates negation (logical NOT) of the test
* mode => indicates whether this is a rule for the source
* or destination port for the rule
*
* Returns: void function
*
***************************************************************************/
void PortToFunc(RuleTreeNode * rtn, int any_flag, int except_flag, int mode)
{
/*
* if the any flag is set we don't need to perform any test to match on
* this port
*/
if (any_flag)
return;
/* if the except_flag is up, test with the "NotEq" funcs */
if (except_flag)
{
switch (mode)
{
case SRC:
#ifdef DEBUG
printf("CheckSrcPortNotEq -> ");
#endif
AddRuleFuncToList(CheckSrcPortNotEq, rtn);
break;
case DST:
#ifdef DEBUG
printf("CheckDstPortNotEq -> ");
#endif
AddRuleFuncToList(CheckDstPortNotEq, rtn);
break;
}
return;
}
/* default to setting the straight test function */
switch (mode)
{
case SRC:
#ifdef DEBUG
printf("CheckSrcPortEqual -> ");
#endif
AddRuleFuncToList(CheckSrcPortEqual, rtn);
break;
case DST:
#ifdef DEBUG
printf("CheckDstPortEqual -> ");
#endif
AddRuleFuncToList(CheckDstPortEqual, rtn);
break;
}
return;
}
/****************************************************************************
*
* 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
*
***************************************************************************/
void AddOptFuncToList(int (*func) (Packet *, struct _OptTreeNode *, struct _OptFpList *), OptTreeNode * otn)
{
OptFpList *idx; /* index pointer */
#ifdef DEBUG
printf("Adding new rule to list\n");
#endif
/* set the index pointer to the start of this OTN's function list */
idx = otn->opt_func;
/* if there are no nodes on the function list... */
if (idx == NULL)
{
/* calloc the list head */
otn->opt_func = (OptFpList *) calloc(sizeof(OptFpList), sizeof(char));
if (otn->opt_func == NULL)
{
FatalError("ERROR => AddOptFuncToList new node calloc failed: %s\n", strerror(errno));
}
/* set the head function */
otn->opt_func->OptTestFunc = func;
} else
{
/* walk to the end of the list */
while (idx->next != NULL)
{
idx = idx->next;
}
/* allocate a new node on the end of the list */
idx->next = (OptFpList *) calloc(sizeof(OptFpList), sizeof(char));
if (idx->next == NULL)
{
FatalError("ERROR => AddOptFuncToList new node calloc failed: %s\n", strerror(errno));
}
/* move up to the new node */
idx = idx->next;
/* link the function to the new node */
idx->OptTestFunc = func;
#ifdef DEBUG
printf("Set OptTestFunc to %p\n", func);
#endif
}
}
/****************************************************************************
*
* Function: ParsePreprocessor(char *)
*
* Purpose: Walks the preprocessor function list looking for the user provided
* keyword. Once found, call the preprocessor's initialization
* function.
*
* Arguments: rule => the preprocessor initialization string from the rules file
*
* Returns: void function
*
***************************************************************************/
void ParsePreprocessor(char *rule)
{
char **toks; /* pointer to the tokenized array parsed from
* the rules list */
char **pp_head; /* parsed keyword list, with preprocessor
* keyword being the 2nd element */
char *funcname; /* the ptr to the actual preprocessor keyword */
char *pp_args = NULL; /* parsed list of arguments to the
* preprocessor */
int num_toks; /* number of tokens returned by the mSplit
* function */
int found = 0; /* flag var */
PreprocessKeywordList *pl_idx; /* index into the preprocessor
* keyword/func list */
/* break out the arguments from the keywords */
toks = mSplit(rule, ":", 2, &num_toks, '\\');
if (num_toks >= 1)
{
#ifdef DEBUG
printf("toks[1] = %s\n", toks[1]);
#endif
/* the args are everything after the ":" */
pp_args = toks[1];
}
/* split the head section for the preprocessor keyword */
pp_head = mSplit(toks[0], " ", 2, &num_toks, '\\');
/* set a pointer to the actual keyword */
funcname = pp_head[1];
/* set the index to the head of the keyword list */
pl_idx = PreprocessKeywords;
/* walk the keyword list */
while (pl_idx != NULL)
{
#ifdef DEBUG
printf("comparing: \"%s\" => \"%s\"\n", funcname, pl_idx->entry.keyword);
#endif
/* compare the keyword against the current list element's keyword */
if (!strcasecmp(funcname, pl_idx->entry.keyword))
{
pl_idx->entry.func(pp_args);
found = 1;
}
if (!found)
{
pl_idx = pl_idx->next;
} else
break;
}
if (!found)
printf("\n*WARNING*: unknown preprocessor \"%s\", ignoring!\n\n",
funcname);
}
void AddFuncToPreprocList(void (*func) (Packet *))
{
PreprocessFuncNode *idx;
idx = PreprocessList;
if (idx == NULL)
{
PreprocessList = (PreprocessFuncNode *) calloc(sizeof(PreprocessFuncNode), sizeof(char));
PreprocessList->func = func;
} else
{
while (idx->next != NULL)
idx = idx->next;
idx->next = (PreprocessFuncNode *) calloc(sizeof(PreprocessFuncNode), sizeof(char));
idx = idx->next;
idx->func = func;
}
return;
}
void ParseOutputPlugin(char *rule)
{
char **toks;
char **pp_head;
char *funcname;
char *pp_args = NULL;
int num_toks;
int found = 0;
OutputKeywordList *pl_idx;
toks = mSplit(rule, ":", 2, &num_toks, '\\');
if (num_toks >= 1)
{
pp_args = toks[1];
}
pp_head = mSplit(toks[0], " ", 2, &num_toks, '\\');
funcname = pp_head[1];
pl_idx = OutputKeywords;
while (pl_idx != NULL)
{
#ifdef DEBUG
printf("comparing: \"%s\" => \"%s\"\n", funcname, pl_idx->entry.keyword);
#endif
if (!strcasecmp(funcname, pl_idx->entry.keyword))
{
switch (pl_idx->entry.node_type)
{
case NT_OUTPUT_ALERT:
if (!pv.alert_cmd_override)
{
if (AlertFunc == NULL)
{
AlertFunc = CallAlertPlugins;
}
/* call the configuration function for the plugin */
pl_idx->entry.func(pp_args);
} else
{
ErrorMessage("WARNING: command line overrides rules file alert plugin!\n");
}
break;
case NT_OUTPUT_LOG:
if (!pv.log_cmd_override)
{
if (LogFunc == NULL)
{
LogFunc = CallLogPlugins;
}
/* call the configuration function for the plugin */
pl_idx->entry.func(pp_args);
} else
{
ErrorMessage("WARNING: command line overrides rules file logging plugin!\n");
}
break;
}
found = 1;
}
if (!found)
{
pl_idx = pl_idx->next;
} else
break;
}
if (!found)
{
printf("\n*WARNING*: unknown output plugin \"%s\", ignoring!\n\n", funcname);
}
}
/*
* frees the existing OutputList ands sets it a single node for the
* function argument
*/
void SetOutputList(void (*func) (Packet *, char *, void *), char node_type,
void *arg)
{
OutputFuncNode *idx;
OutputFuncNode *prev;
switch (node_type)
{
case NT_OUTPUT_ALERT:
prev = AlertList;
break;
case NT_OUTPUT_LOG:
prev = LogList;
break;
default:
return;
}
while (prev != NULL)
{
idx = prev->next;
free(prev);
prev = idx;
}
return AddFuncToOutputList(func, node_type, arg);
}
void AddFuncToOutputList(void (*func) (Packet *, char *, void *), char node_type,
void *arg)
{
switch (node_type)
{
case NT_OUTPUT_ALERT:
if (head_tmp != NULL)
head_tmp->AlertList = AppendOutputFuncList(func, arg,
head_tmp->AlertList);
else
AlertList = AppendOutputFuncList(func, arg, AlertList);
break;
case NT_OUTPUT_LOG:
if (head_tmp != NULL)
head_tmp->LogList = AppendOutputFuncList(func, arg,
head_tmp->LogList);
else
LogList = AppendOutputFuncList(func, arg, LogList);
break;
default:
/* just to be error-prone */
FatalError("Unknown nodetype: %i. Possible bug. please report\n",
node_type);
}
}
OutputFuncNode *AppendOutputFuncList(void (*func) (Packet *, char *, void *),
void *arg, OutputFuncNode * list)
{
OutputFuncNode *idx = list;
if (idx == NULL)
{
idx = (OutputFuncNode *) calloc(sizeof(OutputFuncNode), sizeof(char));
idx->func = func;
idx->arg = arg;
list = idx;
} else
{
while (idx->next != NULL)
idx = idx->next;
idx->next = (OutputFuncNode *) calloc(sizeof(OutputFuncNode), sizeof(char));
idx = idx->next;
idx->func = func;
idx->arg = arg;
}
idx->next = NULL;
return list;
}
/****************************************************************************
*
* 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)
*
* Returns: void function
*
***************************************************************************/
void ParseRuleOptions(char *rule, int rule_type, int protocol)
{
char **toks = NULL;
char **opts;
char *idx;
char *aux;
int num_toks;
int i;
int num_opts;
int found = 0;
OptTreeNode *otn_idx;
KeywordXlateList *kw_idx;
/* set the OTN to the beginning of the list */
otn_idx = rtn_tmp->down;
/*
* make a new one and stick it either at the end of the list or hang it
* off the RTN pointer
*/
if (otn_idx != NULL)
{
/* loop to the end of the list */
while (otn_idx->next != NULL)
{
otn_idx = otn_idx->next;
}
/* setup the new node */
otn_idx->next = (OptTreeNode *) calloc(sizeof(OptTreeNode), sizeof(char));
/* set the global temp ptr */
otn_tmp = otn_idx->next;
if (otn_tmp == NULL)
{
FatalError("ERROR: Unable to alloc OTN: %s", strerror(errno));
}
otn_tmp->next = NULL;
opt_count++;
} else
{
/* first entry on the chain, make a new node and attach it */
otn_idx = (OptTreeNode *) calloc(sizeof(OptTreeNode), sizeof(char));
bzero((char *) otn_idx, sizeof(OptTreeNode));
otn_tmp = otn_idx;
if (otn_tmp == NULL)
{
FatalError("ERROR: Unable to alloc OTN!\n");
}
otn_tmp->next = NULL;
rtn_tmp->down = otn_tmp;
opt_count++;
}
otn_tmp->chain_node_number = opt_count;
otn_tmp->type = rule_type;
/* add link to parent RuleTreeNode */
otn_tmp->rtn = rtn_tmp;
/* find the start of the options block */
idx = index(rule, '(');
i = 0;
if (idx != NULL)
{
idx++;
/* find the end of the options block */
aux = strrchr(idx, ')');
/* get rid of the trailing ")" */
if (aux == NULL)
{
FatalError("ERROR: problems parsing rule file.\n");
}
*aux = 0;
/* seperate all the options out, the seperation token is a semicolon */
/*
* NOTE: if you want to include a semicolon in the content of your
* rule, it must be preceeded with a '\'
*/
toks = mSplit(idx, ";", 10, &num_toks, '\\');
#ifdef DEBUG
printf(" Got %d tokens\n", num_toks);
#endif
/* decrement the number of toks */
num_toks--;
#ifdef DEBUG
printf("Parsing options list: ");
#endif
while (num_toks)
{
#ifdef DEBUG
printf(" option: %s\n", toks[i]);
#endif
/* break out the option name from its data */
opts = mSplit(toks[i], ":", 4, &num_opts, '\\');
#ifdef DEBUG
printf(" option name: %s\n", opts[0]);
printf(" option args: %s\n", opts[1]);
#endif
/* advance to the beginning of the data (past the whitespace) */
while (isspace((int) *opts[0]))
opts[0]++;
/* figure out which option tag we're looking at */
if (!strncasecmp(opts[0], "msg", 3))
{
ParseMessage(opts[1]);
} else if (!strncasecmp(opts[0], "logto", 5))
{
ParseLogto(opts[1]);
} else if (!strncasecmp(opts[0], "activates", 9))
{
ParseActivates(opts[1]);
dynamic_rules_present++;
} else if (!strncasecmp(opts[0], "activated_by", 12))
{
ParseActivatedBy(opts[1]);
dynamic_rules_present++;
} else if (!strncasecmp(opts[0], "count", 5))
{
if (otn_tmp->type != RULE_DYNAMIC)
FatalError("The \"count\" option may only be used with the dynamic rule type!\n");
ParseCount(opts[1]);
}
#ifdef ENABLE_RESPONSE
else if (!strncasecmp(opts[0], "resp", 4))
{
otn_tmp->response_flag = RESP_RST_SND;
ParseResponse(opts[1]);
}
#endif
else
{
kw_idx = KeywordList;
found = 0;
while (kw_idx != NULL)
{
#ifdef DEBUG
printf("comparing: \"%s\" => \"%s\"\n", opts[0], kw_idx->entry.keyword);
#endif
if (!strcasecmp(opts[0], kw_idx->entry.keyword))
{
kw_idx->entry.func(opts[1], otn_tmp, protocol);
found = 1;
#ifdef DEBUG
printf("%s->", kw_idx->entry.keyword);
#endif
}
if (!found)
{
kw_idx = kw_idx->next;
} else
break;
}
if (!found)
{
if (!strcasecmp(opts[0], "minfrag"))
{
FatalError("\nERROR: %s (%d) => Minfrag is no longer a rule option, it is a\npreprocessor (please remove it from your rules file). See RULES.SAMPLE or\nsnort-lib for examples of using the new preprocessors!\n", file_name, file_line);
} else
{
FatalError("\nERROR: %s (%d) => Unknown keyword \"%s\" in rule!\n", file_name, file_line, opts[0]);
}
}
}
free(opts);
--num_toks;
i++;
}
#ifdef DEBUG
printf("OptListEnd\n");
#endif
AddOptFuncToList(OptListEnd, otn_tmp);
} else
{
#ifdef DEBUG
printf("OptListEnd\n");
#endif
AddOptFuncToList(OptListEnd, otn_tmp);
}
free(toks);
}
/****************************************************************************
*
* Function: RuleType(char *)
*
* Purpose: Determine what type of rule is being processed and return its
* equivalent value
*
* Arguments: func => string containing the rule type
*
* Returns: The rule type designation
*
***************************************************************************/
int RuleType(char *func)
{
if (func == NULL)
{
FatalError("ERROR line %s (%d) => Unknown rule type (%s)\n", file_name, file_line, func);
}
if (!strncasecmp(func, "log", 3))
return RULE_LOG;
if (!strncasecmp(func, "alert", 5))
return RULE_ALERT;
if (!strncasecmp(func, "pass", 4))
return RULE_PASS;
if (!strncasecmp(func, "var", 3))
return RULE_VAR;
if (!strncasecmp(func, "include", 7))
return RULE_INCLUDE;
if (!strncasecmp(func, "preprocessor", 12))
return RULE_PREPROCESS;
if (!strncasecmp(func, "output", 6))
return RULE_OUTPUT;
if (!strncasecmp(func, "activate", 8))
return RULE_ACTIVATE;
if (!strncasecmp(func, "dynamic", 7))
return RULE_DYNAMIC;
if (!strncasecmp(func, "config", 6))
return RULE_CONFIG;
if (!strncasecmp(func, "ruletype", 8))
return RULE_DECLARE;
return RULE_UNKNOWN;
}
/****************************************************************************
*
* Function: WhichProto(char *)
*
* Purpose: Figure out which protocol the current rule is talking about
*
* Arguments: proto_str => the protocol string
*
* Returns: The integer value of the protocol
*
***************************************************************************/
int WhichProto(char *proto_str)
{
if (!strncasecmp(proto_str, "tcp", 3))
return IPPROTO_TCP;
if (!strncasecmp(proto_str, "udp", 3))
return IPPROTO_UDP;
if (!strncasecmp(proto_str, "icmp", 4))
return IPPROTO_ICMP;
/*
* if we've gotten here, we have a protocol string we din't recognize and
* should exit
*/
FatalError("ERROR %s (%d) => Bad protocol: %s\n", file_name, file_line, proto_str);
return 0;
}
/****************************************************************************
*
* Function: ParseIP(char *, u_long *, u_long *)
*
* Purpose: Convert a supplied IP address to it's network order 32-bit long
value. Also convert the CIDR block notation into a real
* netmask.
*
* Arguments: addr => address string to convert
* ip_addr => storage point for the converted ip address
* netmask => storage point for the converted netmask
*
* Returns: 0 for normal addresses, 1 for an "any" address
*
***************************************************************************/
int ParseIP(char *paddr, u_long * ip_addr, u_long * netmask)
{
char **toks; /* token dbl buffer */
int num_toks; /* number of tokens found by mSplit() */
int cidr = 1; /* is network expressed in CIDR format */
int nmask; /* netmask temporary storage */
char *addr; /* string to parse, eventually a
* variable-contents */
struct hostent *host_info; /* various struct pointers for stuff */
struct sockaddr_in sin; /* addr struct */
/* check for variable */
if (!strncmp(paddr, "{1}quot;, 1))
{
if ((addr = VarGet(paddr + 1)) == NULL)
{
FatalError("ERROR %s (%d) => Undefined variable %s\n", file_name, file_line, paddr);
}
} else
addr = paddr;
/* check for wildcards */
if (!strncasecmp(addr, "any", 3))
{
*ip_addr = 0;
*netmask = 0;
return 1;
}
/* break out the CIDR notation from the IP address */
toks = mSplit(addr, "/", 2, &num_toks, 0);
/* "/" was not used as a delimeter, try ":" */
if (num_toks == 1)
toks = mSplit(addr, ":", 2, &num_toks, 0);
/*
* if we have a mask spec and it is more than two characters long, assume
* it is netmask format
*/
if ((num_toks > 1) && strlen(toks[1]) > 2)
cidr = 0;
switch (num_toks)
{
case 1:
*netmask = netmasks[32];
break;
case 2:
if (cidr)
{
/* convert the CIDR notation into a real live netmask */
nmask = atoi(toks[1]);
/* it's pain to differ whether toks[1] is correct if netmask */
/* is /0, so we deploy some sort of evil hack with isdigit */
if (!isdigit((int) toks[1][0]))
nmask = -1;
if ((nmask > -1) && (nmask < 33))
{
*netmask = netmasks[nmask];
} else
{
FatalError("ERROR %s (%d) => Invalid CIDR block for IP addr %s\n", file_name, file_line, addr);
}
} else
{
/* convert the netmask into its 32-bit value */
/* broadcast address fix from Steve Beaty <beaty@emess.mscd.edu> */
/*
* * if the address is the (v4) broadcast address, inet_addr *
* returns -1 which usually signifies an error, but in the *
* broadcast address case, is correct. we'd use inet_aton() *
* here, but it's less portable.
*/
if (!strncmp(toks[1], "255.255.255.255", 15))
{
*netmask = INADDR_BROADCAST;
} else if ((*netmask = inet_addr(toks[1])) == -1)
{
FatalError("ERROR %s (%d) => Rule netmask (%s) didn't x-late, WTF?\n", file_name, file_line, toks[1]);
}
}
break;
default:
FatalError("ERROR %s (%d) => Unrecognized IP address/netmask %s\n", file_name, file_line, addr);
break;
}
#ifndef WORDS_BIGENDIAN
/*
* since PC's store things the "wrong" way, shuffle the bytes into the
* right order. Non-CIDR netmasks are already correct.
*/
if (cidr)
{
*netmask = htonl(*netmask);
}
#endif
/* convert names to IP addrs */
if (isalpha((int) toks[0][0]))
{
/* get the hostname and fill in the host_info struct */
if ((host_info = gethostbyname(toks[0])))
{
bcopy(host_info->h_addr, (char *) &sin.sin_addr, host_info->h_length);
} else if ((sin.sin_addr.s_addr = inet_addr(toks[0])) == INADDR_NONE)
{
FatalError("ERROR %s (%d) => Couldn't resolve hostname %s\n",
file_name, file_line, toks[0]);
}
*ip_addr = ((u_long) (sin.sin_addr.s_addr) & (*netmask));
return 1;
}
/* convert the IP addr into its 32-bit value */
/* broadcast address fix from Steve Beaty <beaty@emess.mscd.edu> */
/*
* * if the address is the (v4) broadcast address, inet_addr returns -1 *
* which usually signifies an error, but in the broadcast address case, *
* is correct. we'd use inet_aton() here, but it's less portable.
*/
if (!strncmp(toks[0], "255.255.255.255", 15))
{
*ip_addr = INADDR_BROADCAST;
} else if ((*ip_addr = inet_addr(toks[0])) == -1)
{
FatalError("ERROR %s (%d) => Rule IP addr (%s) didn't x-late, WTF?\n", file_name, file_line, toks[0]);
} else
{
/* set the final homenet address up */
*ip_addr = ((u_long) (*ip_addr) & (*netmask));
}
free(toks);
return 0;
}
/****************************************************************************
*
* Function: ParsePort(char *, u_short *)
*
* Purpose: Convert the port string over to an integer value
*
* Arguments: prule_port => port rule string
* port => converted integer value of the port
*
* Returns: 0 for a normal port number, 1 for an "any" port
*
***************************************************************************/
int ParsePort(char *prule_port, u_short * hi_port, u_short * lo_port, char *proto, int *not_flag)
{
char **toks; /* token dbl buffer */
int num_toks; /* number of tokens found by mSplit() */
char *rule_port; /* port string */
*not_flag = 0;
/* check for variable */
if (!strncmp(prule_port, "{1}quot;, 1))
{
if ((rule_port = VarGet(prule_port + 1)) == NULL)
{
FatalError("ERROR %s (%d) => Undefined variable %s\n", file_name, file_line, prule_port);
}
} else
rule_port = prule_port;
/* check for wildcards */
if (!strncasecmp(rule_port, "any", 3))
{
*hi_port = 0;
*lo_port = 0;
return 1;
}
if (rule_port[0] == '!')
{
*not_flag = 1;
rule_port++;
}
if (rule_port[0] == ':')
{
*lo_port = 0;
}
toks = mSplit(rule_port, ":", 2, &num_toks, 0);
switch (num_toks)
{
case 1:
*hi_port = ConvPort(toks[0], proto);
if (rule_port[0] == ':')
{
*lo_port = 0;
} else
{
*lo_port = *hi_port;
if (index(rule_port, ':') != NULL)
{
*hi_port = 65535;
}
}
return 0;
case 2:
*lo_port = ConvPort(toks[0], proto);
if (toks[1][0] == 0)
*hi_port = 65535;
else
*hi_port = ConvPort(toks[1], proto);
return 0;
default:
FatalError("ERROR %s (%d) => port conversion failed on \"%s\"\n",
file_name, file_line, rule_port);
}
return 0;
}
/****************************************************************************
*
* Function: ConvPort(char *, char *)
*
* Purpose: Convert the port string over to an integer value
*
* Arguments: port => port string
* proto => converted integer value of the port
*
* Returns: the port number
*
***************************************************************************/
int ConvPort(char *port, char *proto)
{
int conv; /* storage for the converted number */
struct servent *service_info;
/*
* convert a "word port" (http, ftp, imap, whatever) to its corresponding
* numeric port value
*/
if (isalpha((int) port[0]) != 0)
{
service_info = getservbyname(port, proto);
if (service_info != NULL)
{
conv = ntohs(service_info->s_port);
return conv;
} else
{
FatalError("ERROR %s (%d) => getservbyname() failed on \"%s\"\n",
file_name, file_line, port);
}
}
if (!isdigit((int) port[0]))
{
FatalError("ERROR %s (%d) => Invalid port: %s\n", file_name,
file_line, port);
}
/* convert the value */
conv = atoi(port);
/* make sure it's in bounds */
if ((conv >= 0) && (conv < 65536))
{
return conv;
} else
{
FatalError("ERROR %s (%d) => bad port number: %s", file_name,
file_line, port);
}
return 0;
}
/****************************************************************************
*
* Function: ParseMessage(char *)
*
* Purpose: Stuff the alert message onto the rule
*
* Arguments: msg => the msg string
*
* Returns: void function
*
***************************************************************************/
void ParseMessage(char *msg)
{
char *ptr;
char *end;
int size;
/* figure out where the message starts */
ptr = index(msg, '"');
if (ptr == NULL)
{
ptr = msg;
} else
ptr++;
end = index(ptr, '"');
if (end != NULL)
*end = 0;
while (isspace((int) *ptr))
ptr++;
/* find the end of the alert string */
size = strlen(msg) + 1;
/* alloc space for the string and put it in the rule */
if (size > 0)
{
otn_tmp->message = (char *) calloc((sizeof(char) * size), sizeof(char));
strncpy(otn_tmp->message, ptr, size);
otn_tmp->message[size - 1] = 0;
#ifdef DEBUG
printf("Rule message set to: %s\n", otn_tmp->message);
#endif
} else
{
ErrorMessage("ERROR %s (%d): bad alert message size %d\n", file_name, file_line, size);
}
}
/****************************************************************************
*
* Function: ParseLogto(char *)
*
* Purpose: stuff the special log filename onto the proper rule option
*
* Arguments: filename => the file name
*
* Returns: void function
*
***************************************************************************/
void ParseLogto(char *filename)
{
char *sptr;
char *eptr;
/* grab everything between the starting " and the end one */
sptr = index(filename, '"');
eptr = strrchr(filename, '"');
if (sptr != NULL && eptr != NULL)
{
/* increment past the first quote */
sptr++;
/* zero out the second one */
*eptr = 0;
} else
{
sptr = filename;
}
/* malloc up a nice shiny clean buffer */
otn_tmp->logto = (char *) calloc(strlen(sptr) + 1, sizeof(char));
bzero((char *) otn_tmp->logto, strlen(sptr) + 1);
strncpy(otn_tmp->logto, sptr, strlen(sptr));
}
#ifdef ENABLE_RESPONSE
/****************************************************************************
*
* Function: ParseResponse(char *)
*
* Purpose: Figure out how to handle hostile connection attempts
*
* Arguments: type => string of comma-sepatared modifiers
*
* Returns: void function
*
***************************************************************************/
void ParseResponse(char *type)
{
char *p;
while (isspace((int) *type))
type++;
if (!type || !(*type))
return;
otn_tmp->response_flag = 0;
p = strtok(type, ",");
while (p)
{
if (!strncasecmp(p, "rst_snd", 7))
otn_tmp->response_flag |= RESP_RST_SND;
else if (!strncasecmp(p, "rst_rcv", 7))
otn_tmp->response_flag |= RESP_RST_RCV;
else if (!strncasecmp(p, "rst_all", 7))
otn_tmp->response_flag |= (RESP_RST_SND | RESP_RST_RCV);
else if (!strncasecmp(p, "icmp_net", 8))
otn_tmp->response_flag |= RESP_BAD_NET;
else if (!strncasecmp(p, "icmp_host", 9))
otn_tmp->response_flag |= RESP_BAD_HOST;
else if (!strncasecmp(p, "icmp_port", 9))
otn_tmp->response_flag |= RESP_BAD_PORT;
else if (!strncasecmp(p, "icmp_all", 9))
otn_tmp->response_flag |= (RESP_BAD_NET | RESP_BAD_HOST | RESP_BAD_PORT);
else
{
FatalError("ERROR %s (%d): invalid response modifier: %s\n", file_name, file_line, p);
}
p = strtok(NULL, ",");
}
}
#endif
/****************************************************************************
*
* Function: ParseActivates(char *)
*
* Purpose: Set an activation link record
*
* Arguments: act_num => rule number to be activated
*
* Returns: void function
*
****************************************************************************/
void ParseActivates(char *act_num)
{
/*
* allocate a new node on the RTN get rid of whitespace at the front of
* the list
*/
while (!isdigit((int) *act_num))
act_num++;
otn_tmp->activates = atoi(act_num);
}
/****************************************************************************
*
* Function: ParseActivatedBy(char *)
*
* Purpose: Set an activation link record
*
* Arguments: act_by => rule number to be activated
*
* Returns: void function
*
****************************************************************************/
void ParseActivatedBy(char *act_by)
{
ActivateList *al_ptr;
al_ptr = rtn_tmp->activate_list;
if (al_ptr == NULL)
{
rtn_tmp->activate_list = (ActivateList *) calloc(sizeof(ActivateList), sizeof(char));
if (rtn_tmp->activate_list == NULL)
{
FatalError("ERROR: ParseActivatedBy() calloc failed: %s\n", strerror(errno));
}
al_ptr = rtn_tmp->activate_list;
} else
{
while (al_ptr->next != NULL)
{
al_ptr = al_ptr->next;
}
al_ptr->next = (ActivateList *) calloc(sizeof(ActivateList), sizeof(char));
al_ptr = al_ptr->next;
if (al_ptr == NULL)
{
FatalError("ERROR: ParseActivatedBy() calloc failed: %s\n", strerror(errno));
}
}
/* get rid of whitespace at the front of the list */
while (!isdigit((int) *act_by))
act_by++;
/* set the RTN list node number */
al_ptr->activated_by = atoi(act_by);
/* set the OTN list node number */
otn_tmp->activated_by = atoi(act_by);
return;
}
void ParseCount(char *num)
{
while (!isdigit((int) *num))
num++;
otn_tmp->activation_counter = atoi(num);
#ifdef DEBUG
printf("Set activation counter to %d\n", otn_tmp->activation_counter);
#endif
return;
}
/****************************************************************************
*
* Function: XferHeader(RuleTreeNode *, RuleTreeNode *)
*
* Purpose: Transfer the rule block header data from point A to point B
*
* Arguments: rule => the place to xfer from
* rtn => the place to xfer to
*
* Returns: void function
*
***************************************************************************/
void XferHeader(RuleTreeNode * rule, RuleTreeNode * rtn)
{
rtn->type = rule->type;
rtn->sip = rule->sip;
rtn->dip = rule->dip;
rtn->smask = rule->smask;
rtn->dmask = rule->dmask;
rtn->hsp = rule->hsp;
rtn->lsp = rule->lsp;
rtn->hdp = rule->hdp;
rtn->ldp = rule->ldp;
rtn->flags = rule->flags;
}
/****************************************************************************
*
* Function: TestHeader(RuleTreeNode *, RuleTreeNode *)
*
* Purpose: Check to see if the two header blocks are identical
*
* Arguments: rule => uh
* rtn => uuuuhhhhh....
*
* Returns: 1 if they match, 0 if they don't
*
***************************************************************************/
int TestHeader(RuleTreeNode * rule, RuleTreeNode * rtn)
{
if (rtn->sip == rule->sip)
{
if (rtn->dip == rule->dip)
{
if (rtn->dmask == rule->dmask)
{
if (rtn->smask == rule->smask)
{
if (rtn->hsp == rule->hsp)
{
if (rtn->lsp == rule->lsp)
{
if (rtn->hdp == rule->hdp)
{
if (rtn->ldp == rule->ldp)
{
if (rtn->flags == rule->flags)
{
return 1;
}
}
}
}
}
}
}
}
}
return 0;
}
/****************************************************************************
*
* Function: VarAlloc()
*
* Purpose: allocates memory for a variable
*
* Arguments: none
*
* Returns: pointer to new VarEntry
*
***************************************************************************/
struct VarEntry *VarAlloc()
{
struct VarEntry *new;
if ((new = (struct VarEntry *) calloc(sizeof(struct VarEntry), sizeof(char))) == NULL)
{
FatalError("ERROR: cannot allocate memory for VarEntry.");
}
new->name = NULL;
new->value = NULL;
new->prev = NULL;
new->next = NULL;
return (new);
}
/****************************************************************************
*
* Function: VarDefine(char *, char *)
*
* Purpose: define the contents of a variable
*
* Arguments: name => the name of the variable
* value => the contents of the variable
*
* Returns: void function
*
***************************************************************************/
void VarDefine(char *name, char *value)
{
struct VarEntry *p;
int found = 0;
if (!VarHead)
{
p = VarAlloc();
p->name = strdup(name);
p->value = strdup(value);
p->prev = p;
p->next = p;
VarHead = p;
return;
}
p = VarHead;
do
{
if (strcasecmp(p->name, name) == 0)
{
found = 1;
break;
}
p = p->next;
} while (p != VarHead);
if (found)
{
if (p->value)
free(p->value);
p->value = strdup(value);
} else
{
p = VarAlloc();
p->name = strdup(name);
p->value = strdup(value);
p->prev = VarHead;
p->next = VarHead->next;
p->next->prev = p;
VarHead->next = p;
}
}
/****************************************************************************
*
* Function: VarDelete(char *)
*
* Purpose: deletes a defined variable
*
* Arguments: name => the name of the variable
*
* Returns: void function
*
***************************************************************************/
void VarDelete(char *name)
{
struct VarEntry *p;
if (!VarHead)
return;
p = VarHead;
do
{
if (strcasecmp(p->name, name) == 0)
{
p->prev->next = p->next;
p->next->prev = p->prev;
if (VarHead == p)
if ((VarHead = p->next) == p)
VarHead = NULL;
if (p->name)
free(p->name);
if (p->value)
free(p->value);
free(p);
return;
}
p = p->next;
} while (p != VarHead);
}
/****************************************************************************
*
* Function: VarGet(char *)
*
* Purpose: get the contents of a variable
*
* Arguments: name => the name of the variable
*
* Returns: char * to contents of variable or NULL
*
***************************************************************************/
char *VarGet(char *name)
{
struct VarEntry *p;
if (!VarHead)
return (NULL);
p = VarHead;
do
{
if (strcasecmp(p->name, name) == 0)
return (p->value);
p = p->next;
} while (p != VarHead);
return (NULL);
}