int ruleMatch(void *p, Rule* rule) { int retVal; SFSnortPacket *sp = (SFSnortPacket *) p; /* Uh, stupid rule... Just checking IPs & Ports apparently */ if (rule->numOptions == 0) return RULE_MATCH; retVal = ruleMatchInternal(sp, rule, 0, NULL); /* Special case for rules that just set a flowbit, but * don't alert. */ if (rule->noAlert) return RULE_NOMATCH; return retVal; }
/* Function to evaluate a loop (ie, a series of nested options) */ ENGINE_LINKAGE int loopEval(void *p, LoopInfo *loop, const u_int8_t **cursor) { const u_int8_t *startingCursor; const u_int8_t *tmpCursor; int32_t i; int32_t startValue; int32_t endValue; int32_t incrementValue; int maxIterations, iterationCount = 0; int ret = RULE_NOMATCH; if (!cursor || !*cursor) return RULE_NOMATCH; /* Protect ourselves... */ if (!loop->initialized) return RULE_NOMATCH; tmpCursor = startingCursor = *cursor; if (loop->start->dynamicType == DYNAMIC_TYPE_INT_STATIC) startValue = loop->start->data.staticInt; else startValue = *(loop->start->data.dynamicInt); if (loop->end->dynamicType == DYNAMIC_TYPE_INT_STATIC) endValue = loop->end->data.staticInt; else endValue = *(loop->end->data.dynamicInt); if (loop->increment->dynamicType == DYNAMIC_TYPE_INT_STATIC) incrementValue = loop->increment->data.staticInt; else incrementValue = *(loop->increment->data.dynamicInt); /* determine max iterations - based on current cursor position, * relative to the appropriate buffer */ maxIterations = getLoopLimit(p, loop, startingCursor); for (i=startValue; checkLoopEnd(loop->op, i, endValue) && (iterationCount < maxIterations); i+= incrementValue) { /* Evaluate the options from the sub rule */ ret = ruleMatchInternal(p, loop->subRule, 0, &tmpCursor); if (ret > RULE_NOMATCH) { *cursor = tmpCursor; return ret; } /* Adjust the starting cursor as specified... */ tmpCursor = startingCursor; ret = setCursor(p, loop->cursorAdjust, &tmpCursor); if (ret != RULE_MATCH) { /* ie, cursor went out of bounds */ return ret; } /* And save off the starting cursor */ startingCursor = tmpCursor; iterationCount++; } return RULE_NOMATCH; }
/* * ruleMatch * * p: packet data structure, same as the one found in snort. * options: NULL terminated list of rule options * * Returns: * > 0 : match found * = 0 : no match found * * Predefined constants: * (see sf_snort_plugin_api.h for more values) * RULE_MATCH - if asn1 specifier is found within buffer * RULE_NOMATCH - if asn1 specifier is not found within buffer * */ int ruleMatchInternal(SFSnortPacket *p, Rule* rule, u_int32_t optIndex, u_int8_t **cursor) { u_int8_t *thisCursor = NULL, *startCursor = NULL; u_int8_t *tmpCursor = NULL; int retVal = RULE_NOMATCH; u_int32_t notFlag = 0; int thisType; ContentInfo *thisContentInfo = NULL; int startAdjust = 0; u_int32_t origFlags = 0; int32_t origOffset = 0; u_int32_t origDepth = 0; int continueLoop = 1; PCREInfo *thisPCREInfo = NULL; if (cursor) startCursor = thisCursor = *cursor; if (optIndex >= rule->numOptions || !rule->options[optIndex] ) return RULE_NOMATCH; thisType = rule->options[optIndex]->optionType; /* Do some saving off of some info for recursion purposes */ switch (thisType) { case OPTION_TYPE_CONTENT: thisContentInfo = rule->options[optIndex]->option_u.content; origFlags = thisContentInfo->flags; origDepth = thisContentInfo->depth; origOffset = thisContentInfo->offset; break; case OPTION_TYPE_PCRE: thisPCREInfo = rule->options[optIndex]->option_u.pcre; origFlags = thisPCREInfo->flags; break; default: /* Other checks should not need to check again like * PCRE & Content do. */ break; } do { switch (thisType) { case OPTION_TYPE_CONTENT: retVal = contentMatch(p, rule->options[optIndex]->option_u.content, &thisCursor); notFlag = rule->options[optIndex]->option_u.content->flags & NOT_FLAG; break; case OPTION_TYPE_PCRE: retVal = pcreMatch(p, rule->options[optIndex]->option_u.pcre, &thisCursor); notFlag = rule->options[optIndex]->option_u.pcre->flags & NOT_FLAG; break; case OPTION_TYPE_FLOWBIT: retVal = processFlowbits(p, rule->options[optIndex]->option_u.flowBit); notFlag = rule->options[optIndex]->option_u.flowBit->flags & NOT_FLAG; break; case OPTION_TYPE_BYTE_TEST: retVal = byteTest(p, rule->options[optIndex]->option_u.byte, thisCursor); notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG; break; case OPTION_TYPE_BYTE_JUMP: retVal = byteJump(p, rule->options[optIndex]->option_u.byte, &thisCursor); notFlag = rule->options[optIndex]->option_u.byte->flags & NOT_FLAG; break; case OPTION_TYPE_FLOWFLAGS: retVal = checkFlow(p, rule->options[optIndex]->option_u.flowFlags); notFlag = rule->options[optIndex]->option_u.flowFlags->flags & NOT_FLAG; break; case OPTION_TYPE_ASN1: retVal = detectAsn1(p, rule->options[optIndex]->option_u.asn1, thisCursor); notFlag = rule->options[optIndex]->option_u.asn1->flags & NOT_FLAG; break; case OPTION_TYPE_CURSOR: retVal = checkCursor(p, rule->options[optIndex]->option_u.cursor, thisCursor); notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG; break; case OPTION_TYPE_SET_CURSOR: retVal = setCursor(p, rule->options[optIndex]->option_u.cursor, &thisCursor); notFlag = rule->options[optIndex]->option_u.cursor->flags & NOT_FLAG; break; case OPTION_TYPE_HDR_CHECK: retVal = checkHdrOpt(p, rule->options[optIndex]->option_u.hdrData); notFlag = rule->options[optIndex]->option_u.hdrData->flags & NOT_FLAG; break; case OPTION_TYPE_BYTE_EXTRACT: retVal = extractValue(p, rule->options[optIndex]->option_u.byteExtract, thisCursor); notFlag = rule->options[optIndex]->option_u.byteExtract->flags & NOT_FLAG; break; case OPTION_TYPE_LOOP: retVal = loopEval(p, rule->options[optIndex]->option_u.loop, &thisCursor); notFlag = rule->options[optIndex]->option_u.loop->flags & NOT_FLAG; break; case OPTION_TYPE_PREPROCESSOR: retVal = preprocOptionEval(p, rule->options[optIndex]->option_u.preprocOpt, &thisCursor); notFlag = rule->options[optIndex]->option_u.preprocOpt->flags & NOT_FLAG; break; } if ( notFlag ) { if ((retVal <= RULE_NOMATCH)) { /* Set this as a positive match -- a ! was specified. */ retVal = RULE_MATCH; } else /* Match */ { retVal = RULE_NOMATCH; } } if (retVal > RULE_NOMATCH) { /* This one matched. Depending on type, check the next one * either in a loop, or not, saving cursor temporarily. */ if (optIndex < rule->numOptions -1) /* hehe, optIndex is 0 based */ { int nestedRetVal; /* Here's where it gets tricky... */ if (thisType == OPTION_TYPE_CONTENT) { /* If this is a content option, we've found a match. * Save off the end-point of the current match. * Less the length of current match plus 1. * * This gives us the starting point to check for this * content again if subsequent options fail. That starting * point is the byte AFTER the beginning of the current * match. */ if ((origFlags & CONTENT_RELATIVE) && startCursor) { /* relative content. * need to adjust offset/depth as well */ tmpCursor = thisCursor - thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength; /* Start Adjust is the difference between the old * starting point and the 'next' starting point. */ startAdjust = tmpCursor - startCursor; } else { /* non-relative content */ tmpCursor = thisCursor - thisContentInfo->patternByteFormLength + thisContentInfo->incrementLength; } } else if (thisType == OPTION_TYPE_PCRE) { /* Start next search at end of current pattern */ /* XXX: Could miss something here if part of pattern * repeats but not easy to tell with PCRE. */ tmpCursor = thisCursor; } nestedRetVal = ruleMatchInternal(p, rule, optIndex+1, &thisCursor); if (nestedRetVal == RULE_MATCH) { /* Cool, everyone after us matched, we're done with a match */ if (cursor) *cursor = thisCursor; break; } /* If Content or PCRE, look farther into the packet * for another match. */ if (((thisType == OPTION_TYPE_CONTENT) || (thisType == OPTION_TYPE_PCRE)) && !notFlag) { /* Only try to find this content again if it is a * positive match. */ /* And only if the next option is relative */ if (!isRelativeOption(rule->options[optIndex+1])) { /* Match failed, next option is not relative. * We're done. */ retVal = nestedRetVal; break; } switch (thisType) { case OPTION_TYPE_CONTENT: if (origFlags & CONTENT_RELATIVE) { if ((int32_t)(origDepth - startAdjust) < (int32_t)thisContentInfo->patternByteFormLength) { /* Adjusted depth would be less than the content we're searching for? * we're done. */ retVal = nestedRetVal; continueLoop = 0; } else { /* For contents that were already relative, adjust the offset & depth fields * from their original values. Makes it easy to determine when we'll be out * of the original bounds, relative to the original cursor. */ thisContentInfo->offset = origOffset + startAdjust; thisContentInfo->depth = origDepth - startAdjust; /* And use the original cursor that was passed in */ thisCursor = startCursor; } } else { thisContentInfo->flags |= CONTENT_RELATIVE; /* For contents that were not already relative, we simply use the adjusted * cursor. Set thisCursor to tmpCursor as calculated above */ thisCursor = tmpCursor; } break; case OPTION_TYPE_PCRE: /* Doesn't matter if it was already relative, * just make it relative anyway. */ thisPCREInfo->flags |= CONTENT_RELATIVE; /* For PCREs that were not already relative, we use the cursor * that was returned at the end of the pattern to start searching * again. */ thisCursor = tmpCursor; break; } continue; } /* Only need to search again when this is a * content option. If its not, we're done. */ if (nestedRetVal <= RULE_NOMATCH) { /* Handle the case when an error is propigated * via nestedRetVal. */ retVal = RULE_NOMATCH; } break; } else { /* Cool, nobody after us, we're done with a match */ if (cursor) *cursor = thisCursor; break; } } else { /* No match, get outta dodge */ break; } } while (continueLoop); /* Keep looping until we break or serialized content checks returns no match. */ /* Reset the flags for this content in case we added the * relative flag above. */ if (thisContentInfo) { thisContentInfo->flags = origFlags; thisContentInfo->offset = origOffset; thisContentInfo->depth = origDepth; } if (thisPCREInfo) { thisPCREInfo->flags = origFlags; } return retVal; }