/** * \brief match the specified filemagic * * \param t thread local vars * \param det_ctx pattern matcher thread local data * \param f *LOCKED* flow * \param flags direction flags * \param file file being inspected * \param s signature being inspected * \param m sigmatch that we will cast into DetectFilemagicData * * \retval 0 no match * \retval 1 match */ static int DetectFilemagicMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m) { SCEnter(); int ret = 0; DetectFilemagicData *filemagic = (DetectFilemagicData *)m->ctx; if (file->txid < det_ctx->tx_id) SCReturnInt(0); if (file->txid > det_ctx->tx_id) SCReturnInt(0); DetectFilemagicThreadData *tfilemagic = (DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, filemagic->thread_ctx_id); if (tfilemagic == NULL) { SCReturnInt(0); } if (file->magic == NULL) { FilemagicThreadLookup(&tfilemagic->ctx, file); } if (file->magic != NULL) { SCLogDebug("magic %s", file->magic); /* we include the \0 in the inspection, so patterns can match on the * end of the string. */ if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic, strlen(file->magic) + 1, filemagic->bm_ctx) != NULL) { #ifdef DEBUG if (SCLogDebugEnabled()) { char *name = SCMalloc(filemagic->len + 1); if (name != NULL) { memcpy(name, filemagic->name, filemagic->len); name[filemagic->len] = '\0'; SCLogDebug("will look for filemagic %s", name); } } #endif if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) { ret = 1; } } else if (filemagic->flags & DETECT_CONTENT_NEGATED) { SCLogDebug("negated match"); ret = 1; } } SCReturnInt(ret); }
/** * \brief match the specified filename * * \param t thread local vars * \param det_ctx pattern matcher thread local data * \param f *LOCKED* flow * \param flags direction flags * \param file file being inspected * \param s signature being inspected * \param m sigmatch that we will cast into DetectFilenameData * * \retval 0 no match * \retval 1 match */ static int DetectFilenameMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m) { SCEnter(); int ret = 0; DetectFilenameData *filename = m->ctx; if (file->name == NULL) SCReturnInt(0); if (file->txid < det_ctx->tx_id) SCReturnInt(0); if (file->txid > det_ctx->tx_id) SCReturnInt(0); if (BoyerMooreNocase(filename->name, filename->len, file->name, file->name_len, filename->bm_ctx->bmGs, filename->bm_ctx->bmBc) != NULL) { #ifdef DEBUG if (SCLogDebugEnabled()) { char *name = SCMalloc(filename->len + 1); if (name != NULL) { memcpy(name, filename->name, filename->len); name[filename->len] = '\0'; SCLogDebug("will look for filename %s", name); } } #endif if (!(filename->flags & DETECT_CONTENT_NEGATED)) { ret = 1; } } if (ret == 0 && (filename->flags & DETECT_CONTENT_NEGATED)) { SCLogDebug("negated match"); ret = 1; } SCReturnInt(ret); }
/** * \brief Run the actual payload match functions * * The following keywords are inspected: * - content, including all the http and dce modified contents * - isdaatat * - pcre * - bytejump * - bytetest * - byte_extract * - urilen * - * * All keywords are evaluated against the buffer with buffer_len. * * For accounting the last match in relative matching the * det_ctx->buffer_offset int is used. * * \param de_ctx Detection engine context * \param det_ctx Detection engine thread context * \param s Signature to inspect * \param sm SigMatch to inspect * \param f Flow (for pcre flowvar storage) * \param buffer Ptr to the buffer to inspect * \param buffer_len Length of the payload * \param stream_start_offset Indicates the start of the current buffer in * the whole buffer stream inspected. This * applies if the current buffer is inspected * in chunks. * \param inspection_mode Refers to the engine inspection mode we are currently * inspecting. Can be payload, stream, one of the http * buffer inspection modes or dce inspection mode. * \param data Used to send some custom data. For example in * payload inspection mode, data contains packet ptr, * and under dce inspection mode, contains dce state. * * \retval 0 no match * \retval 1 match */ int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm, Flow *f, uint8_t *buffer, uint32_t buffer_len, uint32_t stream_start_offset, uint8_t inspection_mode, void *data) { SCEnter(); KEYWORD_PROFILING_START; det_ctx->inspection_recursion_counter++; if (det_ctx->inspection_recursion_counter == de_ctx->inspection_recursion_limit) { det_ctx->discontinue_matching = 1; KEYWORD_PROFILING_END(det_ctx, sm->type, 0); SCReturnInt(0); } if (sm == NULL || buffer_len == 0) { KEYWORD_PROFILING_END(det_ctx, sm->type, 0); SCReturnInt(0); } /* \todo unify this which is phase 2 of payload inspection unification */ if (sm->type == DETECT_CONTENT) { DetectContentData *cd = (DetectContentData *)sm->ctx; SCLogDebug("inspecting content %"PRIu32" buffer_len %"PRIu32, cd->id, buffer_len); /* we might have already have this content matched by the mpm. * (if there is any other reason why we'd want to avoid checking * it here, please fill it in) */ //if (cd->flags & DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED) { // goto match; //} /* rule parsers should take care of this */ #ifdef DEBUG BUG_ON(cd->depth != 0 && cd->depth <= cd->offset); #endif /* search for our pattern, checking the matches recursively. * if we match we look for the next SigMatch as well */ uint8_t *found = NULL; uint32_t offset = 0; uint32_t depth = buffer_len; uint32_t prev_offset = 0; /**< used in recursive searching */ uint32_t prev_buffer_offset = det_ctx->buffer_offset; do { if ((cd->flags & DETECT_CONTENT_DISTANCE) || (cd->flags & DETECT_CONTENT_WITHIN)) { SCLogDebug("det_ctx->buffer_offset %"PRIu32, det_ctx->buffer_offset); offset = prev_buffer_offset; depth = buffer_len; int distance = cd->distance; if (cd->flags & DETECT_CONTENT_DISTANCE) { if (cd->flags & DETECT_CONTENT_DISTANCE_BE) { distance = det_ctx->bj_values[cd->distance]; } if (distance < 0 && (uint32_t)(abs(distance)) > offset) offset = 0; else offset += distance; SCLogDebug("cd->distance %"PRIi32", offset %"PRIu32", depth %"PRIu32, distance, offset, depth); } if (cd->flags & DETECT_CONTENT_WITHIN) { if (cd->flags & DETECT_CONTENT_WITHIN_BE) { if ((int32_t)depth > (int32_t)(prev_buffer_offset + det_ctx->bj_values[cd->within] + distance)) { depth = prev_buffer_offset + det_ctx->bj_values[cd->within] + distance; } } else { if ((int32_t)depth > (int32_t)(prev_buffer_offset + cd->within + distance)) { depth = prev_buffer_offset + cd->within + distance; } SCLogDebug("cd->within %"PRIi32", det_ctx->buffer_offset %"PRIu32", depth %"PRIu32, cd->within, prev_buffer_offset, depth); } if (stream_start_offset != 0 && prev_buffer_offset == 0) { if (depth <= stream_start_offset) { goto no_match; } else if (depth >= (stream_start_offset + buffer_len)) { ; } else { depth = depth - stream_start_offset; } } } if (cd->flags & DETECT_CONTENT_DEPTH_BE) { if ((det_ctx->bj_values[cd->depth] + prev_buffer_offset) < depth) { depth = prev_buffer_offset + det_ctx->bj_values[cd->depth]; } } else { if (cd->depth != 0) { if ((cd->depth + prev_buffer_offset) < depth) { depth = prev_buffer_offset + cd->depth; } SCLogDebug("cd->depth %"PRIu32", depth %"PRIu32, cd->depth, depth); } } if (cd->flags & DETECT_CONTENT_OFFSET_BE) { if (det_ctx->bj_values[cd->offset] > offset) offset = det_ctx->bj_values[cd->offset]; } else { if (cd->offset > offset) { offset = cd->offset; SCLogDebug("setting offset %"PRIu32, offset); } } } else { /* implied no relative matches */ /* set depth */ if (cd->flags & DETECT_CONTENT_DEPTH_BE) { depth = det_ctx->bj_values[cd->depth]; } else { if (cd->depth != 0) { depth = cd->depth; } } if (stream_start_offset != 0 && cd->flags & DETECT_CONTENT_DEPTH) { if (depth <= stream_start_offset) { goto no_match; } else if (depth >= (stream_start_offset + buffer_len)) { ; } else { depth = depth - stream_start_offset; } } /* set offset */ if (cd->flags & DETECT_CONTENT_OFFSET_BE) offset = det_ctx->bj_values[cd->offset]; else offset = cd->offset; prev_buffer_offset = 0; } /* update offset with prev_offset if we're searching for * matches after the first occurence. */ SCLogDebug("offset %"PRIu32", prev_offset %"PRIu32, offset, prev_offset); if (prev_offset != 0) offset = prev_offset; SCLogDebug("offset %"PRIu32", depth %"PRIu32, offset, depth); if (depth > buffer_len) depth = buffer_len; /* if offset is bigger than depth we can never match on a pattern. * We can however, "match" on a negated pattern. */ if (offset > depth || depth == 0) { if (cd->flags & DETECT_CONTENT_NEGATED) { goto match; } else { goto no_match; } } uint8_t *sbuffer = buffer + offset; uint32_t sbuffer_len = depth - offset; uint32_t match_offset = 0; SCLogDebug("sbuffer_len %"PRIu32, sbuffer_len); #ifdef DEBUG BUG_ON(sbuffer_len > buffer_len); #endif /* \todo Add another optimization here. If cd->content_len is * greater than sbuffer_len found is anyways NULL */ /* do the actual search */ if (cd->flags & DETECT_CONTENT_NOCASE) found = BoyerMooreNocase(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx->bmGs, cd->bm_ctx->bmBc); else found = BoyerMoore(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx->bmGs, cd->bm_ctx->bmBc); /* next we evaluate the result in combination with the * negation flag. */ SCLogDebug("found %p cd negated %s", found, cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false"); if (found == NULL && !(cd->flags & DETECT_CONTENT_NEGATED)) { goto no_match; } else if (found == NULL && (cd->flags & DETECT_CONTENT_NEGATED)) { goto match; } else if (found != NULL && (cd->flags & DETECT_CONTENT_NEGATED)) { SCLogDebug("content %"PRIu32" matched at offset %"PRIu32", but negated so no match", cd->id, match_offset); /* don't bother carrying recursive matches now, for preceding * relative keywords */ if (DETECT_CONTENT_IS_SINGLE(cd)) det_ctx->discontinue_matching = 1; goto no_match; } else { match_offset = (uint32_t)((found - buffer) + cd->content_len); SCLogDebug("content %"PRIu32" matched at offset %"PRIu32"", cd->id, match_offset); det_ctx->buffer_offset = match_offset; /* Match branch, add replace to the list if needed */ if (cd->flags & DETECT_CONTENT_REPLACE) { if (inspection_mode == DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD) { /* we will need to replace content if match is confirmed */ det_ctx->replist = DetectReplaceAddToList(det_ctx->replist, found, cd); } else { SCLogWarning(SC_ERR_INVALID_VALUE, "Can't modify payload without packet"); } } if (!(cd->flags & DETECT_CONTENT_RELATIVE_NEXT)) { SCLogDebug("no relative match coming up, so this is a match"); goto match; } /* bail out if we have no next match. Technically this is an * error, as the current cd has the DETECT_CONTENT_RELATIVE_NEXT * flag set. */ if (sm->next == NULL) { goto no_match; } SCLogDebug("content %"PRIu32, cd->id); KEYWORD_PROFILING_END(det_ctx, sm->type, 1); /* see if the next buffer keywords match. If not, we will * search for another occurence of this content and see * if the others match then until we run out of matches */ int r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data); if (r == 1) { SCReturnInt(1); } if (det_ctx->discontinue_matching) goto no_match; /* set the previous match offset to the start of this match + 1 */ prev_offset = (match_offset - (cd->content_len - 1)); SCLogDebug("trying to see if there is another match after prev_offset %"PRIu32, prev_offset); } } while(1); } else if (sm->type == DETECT_ISDATAAT) { SCLogDebug("inspecting isdataat"); DetectIsdataatData *id = (DetectIsdataatData *)sm->ctx; if (id->flags & ISDATAAT_RELATIVE) { if (det_ctx->buffer_offset + id->dataat > buffer_len) { SCLogDebug("det_ctx->buffer_offset + id->dataat %"PRIu32" > %"PRIu32, det_ctx->buffer_offset + id->dataat, buffer_len); if (id->flags & ISDATAAT_NEGATED) goto match; goto no_match; } else { SCLogDebug("relative isdataat match"); if (id->flags & ISDATAAT_NEGATED) goto no_match; goto match; } } else { if (id->dataat < buffer_len) { SCLogDebug("absolute isdataat match"); if (id->flags & ISDATAAT_NEGATED) goto no_match; goto match; } else { SCLogDebug("absolute isdataat mismatch, id->isdataat %"PRIu32", buffer_len %"PRIu32"", id->dataat, buffer_len); if (id->flags & ISDATAAT_NEGATED) goto match; goto no_match; } } } else if (sm->type == DETECT_PCRE) { SCLogDebug("inspecting pcre"); DetectPcreData *pe = (DetectPcreData *)sm->ctx; uint32_t prev_buffer_offset = det_ctx->buffer_offset; uint32_t prev_offset = 0; int r = 0; det_ctx->pcre_match_start_offset = 0; do { Packet *p = NULL; if (inspection_mode == DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD) p = (Packet *)data; r = DetectPcrePayloadMatch(det_ctx, s, sm, p, f, buffer, buffer_len); if (r == 0) { goto no_match; } if (!(pe->flags & DETECT_PCRE_RELATIVE_NEXT)) { SCLogDebug("no relative match coming up, so this is a match"); goto match; } KEYWORD_PROFILING_END(det_ctx, sm->type, 1); /* save it, in case we need to do a pcre match once again */ prev_offset = det_ctx->pcre_match_start_offset; /* see if the next payload keywords match. If not, we will * search for another occurence of this pcre and see * if the others match, until we run out of matches */ r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data); if (r == 1) { SCReturnInt(1); } if (det_ctx->discontinue_matching) goto no_match; det_ctx->buffer_offset = prev_buffer_offset; det_ctx->pcre_match_start_offset = prev_offset; } while (1); } else if (sm->type == DETECT_BYTETEST) { DetectBytetestData *btd = (DetectBytetestData *)sm->ctx; uint8_t flags = btd->flags; int32_t offset = btd->offset; uint64_t value = btd->value; if (flags & DETECT_BYTETEST_OFFSET_BE) { offset = det_ctx->bj_values[offset]; } if (flags & DETECT_BYTETEST_VALUE_BE) { value = det_ctx->bj_values[value]; } /* if we have dce enabled we will have to use the endianness * specified by the dce header */ if (flags & DETECT_BYTETEST_DCE) { DCERPCState *dcerpc_state = (DCERPCState *)data; /* enable the endianness flag temporarily. once we are done * processing we reset the flags to the original value*/ flags |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] & 0x10) ? DETECT_BYTETEST_LITTLE: 0); } if (DetectBytetestDoMatch(det_ctx, s, sm, buffer, buffer_len, flags, offset, value) != 1) { goto no_match; } goto match; } else if (sm->type == DETECT_BYTEJUMP) { DetectBytejumpData *bjd = (DetectBytejumpData *)sm->ctx; uint8_t flags = bjd->flags; int32_t offset = bjd->offset; if (flags & DETECT_BYTEJUMP_OFFSET_BE) { offset = det_ctx->bj_values[offset]; } /* if we have dce enabled we will have to use the endianness * specified by the dce header */ if (flags & DETECT_BYTEJUMP_DCE) { DCERPCState *dcerpc_state = (DCERPCState *)data; /* enable the endianness flag temporarily. once we are done * processing we reset the flags to the original value*/ flags |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] & 0x10) ? DETECT_BYTEJUMP_LITTLE: 0); } if (DetectBytejumpDoMatch(det_ctx, s, sm, buffer, buffer_len, flags, offset) != 1) { goto no_match; } goto match; } else if (sm->type == DETECT_BYTE_EXTRACT) { DetectByteExtractData *bed = (DetectByteExtractData *)sm->ctx; uint8_t endian = bed->endian; /* if we have dce enabled we will have to use the endianness * specified by the dce header */ if ((bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) && endian == DETECT_BYTE_EXTRACT_ENDIAN_DCE) { DCERPCState *dcerpc_state = (DCERPCState *)data; /* enable the endianness flag temporarily. once we are done * processing we reset the flags to the original value*/ endian |= ((dcerpc_state->dcerpc.dcerpchdr.packed_drep[0] == 0x10) ? DETECT_BYTE_EXTRACT_ENDIAN_LITTLE : DETECT_BYTE_EXTRACT_ENDIAN_BIG); } if (DetectByteExtractDoMatch(det_ctx, sm, s, buffer, buffer_len, &det_ctx->bj_values[bed->local_id], endian) != 1) { goto no_match; } goto match; /* we should never get here, but bail out just in case */ } else if (sm->type == DETECT_AL_URILEN) { SCLogDebug("inspecting uri len"); int r = 0; DetectUrilenData *urilend = (DetectUrilenData *) sm->ctx; switch (urilend->mode) { case DETECT_URILEN_EQ: if (buffer_len == urilend->urilen1) r = 1; break; case DETECT_URILEN_LT: if (buffer_len < urilend->urilen1) r = 1; break; case DETECT_URILEN_GT: if (buffer_len > urilend->urilen1) r = 1; break; case DETECT_URILEN_RA: if (buffer_len > urilend->urilen1 && buffer_len < urilend->urilen2) { r = 1; } break; } if (r == 1) { goto match; } det_ctx->discontinue_matching = 0; goto no_match; #ifdef HAVE_LUAJIT } else if (sm->type == DETECT_LUAJIT) { /* for flowvar gets and sets we need to know the flow's lock status */ int need_flow_lock = 0; if (inspection_mode <= DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM) need_flow_lock = 1; if (DetectLuajitMatchBuffer(det_ctx, s, sm, buffer, buffer_len, det_ctx->buffer_offset, f, need_flow_lock) != 1) { goto no_match; } goto match; #endif } else { SCLogDebug("sm->type %u", sm->type); #ifdef DEBUG BUG_ON(1); #endif } no_match: KEYWORD_PROFILING_END(det_ctx, sm->type, 0); SCReturnInt(0); match: /* this sigmatch matched, inspect the next one. If it was the last, * the buffer portion of the signature matched. */ if (sm->next != NULL) { KEYWORD_PROFILING_END(det_ctx, sm->type, 1); int r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data); SCReturnInt(r); } else { KEYWORD_PROFILING_END(det_ctx, sm->type, 1); SCReturnInt(1); } }