/* Copyright (C) 2017-2022 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 * * Implements the bsize generic buffer length keyword */ #include "suricata-common.h" #include "util-unittest.h" #include "util-unittest-helper.h" #include "detect.h" #include "detect-parse.h" #include "detect-engine.h" #include "detect-content.h" #include "detect-engine-uint.h" #include "detect-bsize.h" #include "util-misc.h" /*prototypes*/ static int DetectBsizeSetup (DetectEngineCtx *, Signature *, const char *); static void DetectBsizeFree (DetectEngineCtx *, void *); static int SigParseGetMaxBsize(DetectU64Data *bsz); #ifdef UNITTESTS static void DetectBsizeRegisterTests (void); #endif bool DetectBsizeValidateContentCallback(Signature *s, const SignatureInitDataBuffer *b) { int bsize = -1; DetectU64Data *bsz; for (const SigMatch *sm = b->head; sm != NULL; sm = sm->next) { if (sm->type == DETECT_BSIZE) { bsz = (DetectU64Data *)sm->ctx; bsize = SigParseGetMaxBsize(bsz); break; } } if (bsize == -1) { return true; } uint64_t needed; if (bsize >= 0) { int len, offset; SigParseRequiredContentSize(s, bsize, b->head, &len, &offset); SCLogDebug("bsize: %d; len: %d; offset: %d [%s]", bsize, len, offset, s->sig_str); needed = len; if (len > bsize) { goto value_error; } if ((len + offset) > bsize) { needed += offset; goto value_error; } } return true; value_error: if (bsz->mode == DETECT_UINT_RA) { SCLogError("signature can't match as required content length %" PRIu64 " exceeds bsize range: %" PRIu64 "-%" PRIu64, needed, bsz->arg1, bsz->arg2); } else { SCLogError("signature can't match as required content length %" PRIu64 " exceeds bsize value: " "%" PRIu64, needed, bsz->arg1); } return false; } /** * \brief Registration function for bsize: keyword */ void DetectBsizeRegister(void) { sigmatch_table[DETECT_BSIZE].name = "bsize"; sigmatch_table[DETECT_BSIZE].desc = "match on the length of a buffer"; sigmatch_table[DETECT_BSIZE].url = "/rules/payload-keywords.html#bsize"; sigmatch_table[DETECT_BSIZE].Match = NULL; sigmatch_table[DETECT_BSIZE].Setup = DetectBsizeSetup; sigmatch_table[DETECT_BSIZE].Free = DetectBsizeFree; #ifdef UNITTESTS sigmatch_table[DETECT_BSIZE].RegisterTests = DetectBsizeRegisterTests; #endif } /** \brief bsize match function * * \param ctx match ctx * \param buffer_size size of the buffer * \param eof is the buffer closed? * * \retval r 1 match, 0 no match, -1 can't match */ int DetectBsizeMatch(const SigMatchCtx *ctx, const uint64_t buffer_size, bool eof) { const DetectU64Data *bsz = (const DetectU64Data *)ctx; if (DetectU64Match(buffer_size, bsz)) { return 1; } switch (bsz->mode) { case DETECT_UINT_LTE: return -1; case DETECT_UINT_LT: return -1; case DETECT_UINT_GTE: // fallthrough case DETECT_UINT_GT: if (eof) { return -1; } return 0; case DETECT_UINT_EQ: if (buffer_size > bsz->arg1) { return -1; } else if (eof) { return -1; } else { return 0; } case DETECT_UINT_RA: if (buffer_size <= bsz->arg1 && eof) { return -1; } else if (buffer_size <= bsz->arg1) { return 0; } else if (buffer_size >= bsz->arg2) { return -1; } } return 0; } /** * \brief This function is used to parse bsize options passed via bsize: keyword * * \param bsizestr Pointer to the user provided bsize options * * \retval bsized pointer to DetectU64Data on success * \retval NULL on failure */ static DetectU64Data *DetectBsizeParse(const char *str) { return DetectU64Parse(str); } static int SigParseGetMaxBsize(DetectU64Data *bsz) { switch (bsz->mode) { case DETECT_UINT_LT: case DETECT_UINT_EQ: return bsz->arg1; case DETECT_UINT_RA: return bsz->arg2; case DETECT_UINT_GT: default: SCReturnInt(-2); } SCReturnInt(-1); } /** * \brief this function is used to parse bsize data into the current signature * * \param de_ctx pointer to the Detection Engine Context * \param s pointer to the Current Signature * \param sizestr pointer to the user provided bsize options * * \retval 0 on Success * \retval -1 on Failure */ static int DetectBsizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *sizestr) { SCEnter(); SigMatch *sm = NULL; if (DetectBufferGetActiveList(de_ctx, s) == -1) SCReturnInt(-1); int list = s->init_data->list; if (list == DETECT_SM_LIST_NOTSET) SCReturnInt(-1); DetectU64Data *bsz = DetectBsizeParse(sizestr); if (bsz == NULL) goto error; sm = SigMatchAlloc(); if (sm == NULL) goto error; sm->type = DETECT_BSIZE; sm->ctx = (void *)bsz; SigMatchAppendSMToList(s, sm, list); SCReturnInt(0); error: DetectBsizeFree(de_ctx, bsz); SCReturnInt(-1); } /** * \brief this function will free memory associated with DetectU64Data * * \param ptr pointer to DetectU64Data */ void DetectBsizeFree(DetectEngineCtx *de_ctx, void *ptr) { if (ptr == NULL) return; DetectU64Data *bsz = (DetectU64Data *)ptr; rs_detect_u64_free(bsz); } #ifdef UNITTESTS #include "tests/detect-bsize.c" #endif