/* Copyright (C) 2007-2010 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /** * \file * * \author Victor Julien * \author Anoop Saldanha * * Implements the distance keyword */ #include "suricata-common.h" #include "decode.h" #include "detect.h" #include "detect-parse.h" #include "detect-engine.h" #include "app-layer.h" #include "detect-content.h" #include "detect-uricontent.h" #include "detect-pcre.h" #include "detect-byte.h" #include "detect-byte-extract.h" #include "detect-distance.h" #include "flow-var.h" #include "util-byte.h" #include "util-debug.h" #include "util-unittest.h" #include "detect-bytejump.h" #include "util-unittest-helper.h" static int DetectDistanceSetup(DetectEngineCtx *, Signature *, const char *); #ifdef UNITTESTS static void DetectDistanceRegisterTests(void); #endif void DetectDistanceRegister(void) { sigmatch_table[DETECT_DISTANCE].name = "distance"; sigmatch_table[DETECT_DISTANCE].desc = "indicates a relation between this content keyword and the content preceding it"; sigmatch_table[DETECT_DISTANCE].url = "/rules/payload-keywords.html#distance"; sigmatch_table[DETECT_DISTANCE].Match = NULL; sigmatch_table[DETECT_DISTANCE].Setup = DetectDistanceSetup; sigmatch_table[DETECT_DISTANCE].Free = NULL; #ifdef UNITTESTS sigmatch_table[DETECT_DISTANCE].RegisterTests = DetectDistanceRegisterTests; #endif } static int DetectDistanceSetup (DetectEngineCtx *de_ctx, Signature *s, const char *distancestr) { const char *str = distancestr; SigMatch *pm = NULL; int ret = -1; /* retrieve the sm to apply the distance against */ pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1); if (pm == NULL) { SCLogError(SC_ERR_OFFSET_MISSING_CONTENT, "distance needs " "preceding content, uricontent option, http_client_body, " "http_server_body, http_header option, http_raw_header option, " "http_method option, http_cookie, http_raw_uri, " "http_stat_msg, http_stat_code, http_user_agent or " "file_data/dce_stub_data sticky buffer option"); goto end; } /* verify other conditions */ DetectContentData *cd = (DetectContentData *)pm->ctx; if (cd->flags & DETECT_CONTENT_DISTANCE) { SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple distances for the same content."); goto end; } if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) { SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative " "keyword like within/distance with a absolute " "relative keyword like depth/offset for the same " "content." ); goto end; } if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) { SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative " "negated keyword set along with a fast_pattern"); goto end; } if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) { SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative " "keyword set along with a fast_pattern:only;"); goto end; } if (str[0] != '-' && isalpha((unsigned char)str[0])) { DetectByteIndexType index; if (!DetectByteRetrieveSMVar(str, s, &index)) { SCLogError(SC_ERR_INVALID_SIGNATURE, "unknown byte_ keyword var " "seen in distance - %s\n", str); goto end; } cd->distance = index; cd->flags |= DETECT_CONTENT_DISTANCE_VAR; } else { if (StringParseInt32(&cd->distance, 0, 0, str) < 0) { SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid value for distance: %s", str); goto end; } } cd->flags |= DETECT_CONTENT_DISTANCE; SigMatch *prev_pm = DetectGetLastSMByListPtr(s, pm->prev, DETECT_CONTENT, DETECT_PCRE, -1); if (prev_pm == NULL) { ret = 0; goto end; } if (prev_pm->type == DETECT_CONTENT) { DetectContentData *prev_cd = (DetectContentData *)prev_pm->ctx; if (prev_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) { SCLogError(SC_ERR_INVALID_SIGNATURE, "previous keyword " "has a fast_pattern:only; set. Can't " "have relative keywords around a fast_pattern " "only content"); goto end; } if ((cd->flags & DETECT_CONTENT_NEGATED) == 0) { prev_cd->flags |= DETECT_CONTENT_DISTANCE_NEXT; } else { prev_cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; } } else if (prev_pm->type == DETECT_PCRE) { DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx; pd->flags |= DETECT_PCRE_RELATIVE_NEXT; } ret = 0; end: return ret; } #ifdef UNITTESTS static int DetectDistanceTest01(void) { int result = 0; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { printf("no de_ctx: "); goto end; } de_ctx->flags |= DE_QUIET; de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (content:\"|AA BB|\"; content:\"|CC DD EE FF 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE|\"; distance: 4; within: 19; sid:1; rev:1;)"); if (de_ctx->sig_list == NULL) { printf("sig parse failed: "); goto end; } SigMatch *sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH]; if (sm == NULL) { printf("sm NULL: "); goto end; } sm = sm->next; if (sm == NULL) { printf("sm2 NULL: "); goto end; } DetectContentData *co = (DetectContentData *)sm->ctx; if (co == NULL) { printf("co == NULL: "); goto end; } if (co->distance != 4) { printf("distance %"PRIi32", expected 4: ", co->distance); goto end; } /* within needs to be 23: distance + content_len as Snort auto fixes this */ if (co->within != 19) { printf("within %"PRIi32", expected 23: ", co->within); goto end; } result = 1; end: DetectEngineCtxFree(de_ctx); return result; } /** * \test DetectDistanceTestPacket01 is a test to check matches of * distance works, if the previous keyword is byte_jump and content * (bug 163) */ static int DetectDistanceTestPacket01 (void) { int result = 0; uint8_t buf[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint16_t buflen = sizeof(buf); Packet *p; p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); if (p == NULL) goto end; char sig[] = "alert tcp any any -> any any (msg:\"suricata test\"; " "byte_jump:1,2; content:\"|00|\"; " "within:1; distance:2; sid:98711212; rev:1;)"; p->flowflags = FLOW_PKT_ESTABLISHED | FLOW_PKT_TOCLIENT; result = UTHPacketMatchSig(p, sig); UTHFreePacket(p); end: return result; } static void DetectDistanceRegisterTests(void) { UtRegisterTest("DetectDistanceTest01 -- distance / within mix", DetectDistanceTest01); UtRegisterTest("DetectDistanceTestPacket01", DetectDistanceTestPacket01); } #endif /* UNITTESTS */