suricata
detect-fragoffset.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2021 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Breno Silva <breno.silva@gmail.com>
22 *
23 * Implements fragoffset keyword
24 */
25
26#include "suricata-common.h"
27#include "decode.h"
28#include "decode-ipv4.h"
29#include "decode-ipv6.h"
30
31#include "detect.h"
32#include "detect-parse.h"
34#include "detect-engine-build.h"
35
36#include "detect-fragoffset.h"
37
38#include "util-byte.h"
39#include "util-unittest.h"
40#include "util-debug.h"
41
42#define PARSE_REGEX "^\\s*(?:(<|>))?\\s*([0-9]+)"
43
44static DetectParseRegex parse_regex;
45
46static int DetectFragOffsetMatch(DetectEngineThreadCtx *,
47 Packet *, const Signature *, const SigMatchCtx *);
48static int DetectFragOffsetSetup(DetectEngineCtx *, Signature *, const char *);
49#ifdef UNITTESTS
51#endif
53
54static int PrefilterSetupFragOffset(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
55static bool PrefilterFragOffsetIsPrefilterable(const Signature *s);
56
57/**
58 * \brief Registration function for fragoffset
59 */
61{
63 sigmatch_table[DETECT_FRAGOFFSET].desc = "match on specific decimal values of the IP fragment offset field";
64 sigmatch_table[DETECT_FRAGOFFSET].url = "/rules/header-keywords.html#fragoffset";
65 sigmatch_table[DETECT_FRAGOFFSET].Match = DetectFragOffsetMatch;
66 sigmatch_table[DETECT_FRAGOFFSET].Setup = DetectFragOffsetSetup;
68#ifdef UNITTESTS
70#endif
71 sigmatch_table[DETECT_FRAGOFFSET].SupportsPrefilter = PrefilterFragOffsetIsPrefilterable;
72 sigmatch_table[DETECT_FRAGOFFSET].SetupPrefilter = PrefilterSetupFragOffset;
73
75}
76
77static inline int FragOffsetMatch(const uint16_t poffset, const uint8_t mode,
78 const uint16_t doffset)
79{
80 switch (mode) {
81 case FRAG_LESS:
82 if (poffset < doffset)
83 return 1;
84 break;
85 case FRAG_MORE:
86 if (poffset > doffset)
87 return 1;
88 break;
89 default:
90 if (poffset == doffset)
91 return 1;
92 }
93 return 0;
94}
95
96/**
97 * \brief This function is used to match fragoffset rule option set on a packet
98 *
99 * \param t pointer to thread vars
100 * \param det_ctx pointer to the pattern matcher thread
101 * \param p pointer to the current packet
102 * \param m pointer to the sigmatch that we will cast into DetectFragOffsetData
103 *
104 * \retval 0 no match or frag is not set
105 * \retval 1 match
106 *
107 */
108static int DetectFragOffsetMatch (DetectEngineThreadCtx *det_ctx,
109 Packet *p, const Signature *s, const SigMatchCtx *ctx)
110{
111 uint16_t frag = 0;
112 const DetectFragOffsetData *fragoff = (const DetectFragOffsetData *)ctx;
113
115
116 if (PacketIsIPv4(p)) {
117 const IPV4Hdr *ip4h = PacketGetIPv4(p);
118 frag = IPV4_GET_RAW_FRAGOFFSET(ip4h);
119 } else if (PacketIsIPv6(p)) {
120 if (IPV6_EXTHDR_ISSET_FH(p)) {
122 } else {
123 return 0;
124 }
125 } else {
126 SCLogDebug("No IPv4 or IPv6 packet");
127 return 0;
128 }
129
130 return FragOffsetMatch(frag, fragoff->mode, fragoff->frag_off);
131}
132
133/**
134 * \brief This function is used to parse fragoffset option passed via fragoffset: keyword
135 *
136 * \param de_ctx Pointer to the detection engine context
137 * \param fragoffsetstr Pointer to the user provided fragoffset options
138 *
139 * \retval fragoff pointer to DetectFragOffsetData on success
140 * \retval NULL on failure
141 */
142static DetectFragOffsetData *DetectFragOffsetParse (DetectEngineCtx *de_ctx, const char *fragoffsetstr)
143{
144 DetectFragOffsetData *fragoff = NULL;
145 char *substr[3] = {NULL, NULL, NULL};
146 int res = 0;
147 size_t pcre2_len;
148 int i;
149 const char *str_ptr;
150 char *mode = NULL;
151 pcre2_match_data *match = NULL;
152
153 int ret = DetectParsePcreExec(&parse_regex, &match, fragoffsetstr, 0, 0);
154 if (ret < 1 || ret > 4) {
155 SCLogError("Parse error %s", fragoffsetstr);
156 goto error;
157 }
158
159 for (i = 1; i < ret; i++) {
160 res = SC_Pcre2SubstringGet(match, i, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
161 if (res < 0) {
162 SCLogError("pcre2_substring_get_bynumber failed");
163 goto error;
164 }
165 substr[i-1] = (char *)str_ptr;
166 }
167
168 fragoff = SCMalloc(sizeof(DetectFragOffsetData));
169 if (unlikely(fragoff == NULL))
170 goto error;
171
172 fragoff->frag_off = 0;
173 fragoff->mode = 0;
174
175 mode = substr[0];
176
177 if(mode != NULL) {
178
179 while(*mode != '\0') {
180 switch(*mode) {
181 case '>':
182 fragoff->mode = FRAG_MORE;
183 break;
184 case '<':
185 fragoff->mode = FRAG_LESS;
186 break;
187 }
188 mode++;
189 }
190 }
191
192 if (StringParseUint16(&fragoff->frag_off, 10, 0, substr[1]) < 0) {
193 SCLogError("specified frag offset %s is not "
194 "valid",
195 substr[1]);
196 goto error;
197 }
198
199 for (i = 0; i < 3; i++) {
200 if (substr[i] != NULL)
201 pcre2_substring_free((PCRE2_UCHAR8 *)substr[i]);
202 }
203
204 pcre2_match_data_free(match);
205 return fragoff;
206
207error:
208 if (match) {
209 pcre2_match_data_free(match);
210 }
211 for (i = 0; i < 3; i++) {
212 if (substr[i] != NULL)
213 pcre2_substring_free((PCRE2_UCHAR8 *)substr[i]);
214 }
215 if (fragoff != NULL) DetectFragOffsetFree(de_ctx, fragoff);
216 return NULL;
217
218}
219
220/**
221 * \brief this function is used to add the parsed fragoffset data into the current signature
222 *
223 * \param de_ctx pointer to the Detection Engine Context
224 * \param s pointer to the Current Signature
225 * \param fragoffsetstr pointer to the user provided fragoffset option
226 *
227 * \retval 0 on Success
228 * \retval -1 on Failure
229 */
230static int DetectFragOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *fragoffsetstr)
231{
232 DetectFragOffsetData *fragoff = NULL;
233
234 fragoff = DetectFragOffsetParse(de_ctx, fragoffsetstr);
235 if (fragoff == NULL) goto error;
236
238 DETECT_SM_LIST_MATCH) == NULL) {
239 goto error;
240 }
242
243 return 0;
244
245error:
246 if (fragoff != NULL)
248 return -1;
249
250}
251
252/**
253 * \brief this function will free memory associated with DetectFragOffsetData
254 *
255 * \param ptr pointer to DetectFragOffsetData
256 */
258{
260 SCFree(fragoff);
261}
262
263static void
264PrefilterPacketFragOffsetMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
265{
267
268 uint16_t frag;
269
270 if (PacketIsIPv4(p)) {
271 const IPV4Hdr *ip4h = PacketGetIPv4(p);
272 frag = IPV4_GET_RAW_FRAGOFFSET(ip4h);
273 } else if (PacketIsIPv6(p)) {
274 if (IPV6_EXTHDR_ISSET_FH(p)) {
276 } else {
277 return;
278 }
279 } else {
280 SCLogDebug("No IPv4 or IPv6 packet");
281 return;
282 }
283
284 const PrefilterPacketHeaderCtx *ctx = pectx;
285 if (FragOffsetMatch(frag, ctx->v1.u8[0], ctx->v1.u16[1]))
286 {
287 PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
288 }
289}
290
291static void
292PrefilterPacketFragOffsetSet(PrefilterPacketHeaderValue *v, void *smctx)
293{
294 const DetectFragOffsetData *fb = smctx;
295 v->u8[0] = fb->mode;
296 v->u16[1] = fb->frag_off;
297}
298
299static bool
300PrefilterPacketFragOffsetCompare(PrefilterPacketHeaderValue v, void *smctx)
301{
302 const DetectFragOffsetData *fb = smctx;
303 if (v.u8[0] == fb->mode &&
304 v.u16[1] == fb->frag_off)
305 {
306 return true;
307 }
308 return false;
309}
310
311static int PrefilterSetupFragOffset(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
312{
314 PrefilterPacketFragOffsetSet, PrefilterPacketFragOffsetCompare,
315 PrefilterPacketFragOffsetMatch);
316}
317
318static bool PrefilterFragOffsetIsPrefilterable(const Signature *s)
319{
320 const SigMatch *sm;
321 for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
322 switch (sm->type) {
324 return true;
325 }
326 }
327 return false;
328}
329
330#ifdef UNITTESTS
331#include "util-unittest-helper.h"
332#include "detect-engine.h"
333#include "detect-engine-alert.h"
334
335/**
336 * \test DetectFragOffsetParseTest01 is a test for setting a valid fragoffset value
337 */
338static int DetectFragOffsetParseTest01 (void)
339{
340 DetectFragOffsetData *fragoff = DetectFragOffsetParse(NULL, "300");
341
342 FAIL_IF_NULL(fragoff);
343 FAIL_IF_NOT(fragoff->frag_off == 300);
344
345 DetectFragOffsetFree(NULL, fragoff);
346
347 PASS;
348}
349
350/**
351 * \test DetectFragOffsetParseTest02 is a test for setting a valid fragoffset value
352 * with spaces all around
353 */
354static int DetectFragOffsetParseTest02 (void)
355{
356 DetectFragOffsetData *fragoff = DetectFragOffsetParse(NULL, ">300");
357
358 FAIL_IF_NULL(fragoff);
359 FAIL_IF_NOT(fragoff->frag_off == 300);
360 FAIL_IF_NOT(fragoff->mode == FRAG_MORE);
361
362 DetectFragOffsetFree(NULL, fragoff);
363
364 PASS;
365}
366
367/**
368 * \test DetectFragOffsetParseTest03 is a test for setting an invalid fragoffset value
369 */
370static int DetectFragOffsetParseTest03 (void)
371{
372 DetectFragOffsetData *fragoff = DetectFragOffsetParse(NULL, "badc");
373
374 FAIL_IF_NOT_NULL(fragoff);
375
376 PASS;
377}
378
379/**
380 * \test DetectFragOffsetMatchTest01 is a test for checking the working of
381 * fragoffset keyword by creating 2 rules and matching a crafted packet
382 * against them. Only the first one shall trigger.
383 */
384static int DetectFragOffsetMatchTest01 (void)
385{
387
388 FAIL_IF_NULL(p);
389 Signature *s = NULL;
391 ThreadVars th_v;
392 DetectEngineThreadCtx *det_ctx = NULL;
393 IPV4Hdr ip4h;
394
395 memset(&ip4h, 0, sizeof(IPV4Hdr));
396 memset(&dtv, 0, sizeof(DecodeThreadVars));
397 memset(&th_v, 0, sizeof(ThreadVars));
398
400
401 p->src.family = AF_INET;
402 p->dst.family = AF_INET;
403 p->src.addr_data32[0] = 0x01020304;
404 p->dst.addr_data32[0] = 0x04030201;
405
406 ip4h.s_ip_src.s_addr = p->src.addr_data32[0];
407 ip4h.s_ip_dst.s_addr = p->dst.addr_data32[0];
408 ip4h.ip_off = 0x2222;
409 UTHSetIPV4Hdr(p, &ip4h);
410
413
415
416 s = DetectEngineAppendSig(de_ctx, "alert ip any any -> any any (fragoffset:546; sid:1;)");
417 FAIL_IF_NULL(s);
418
419 s = DetectEngineAppendSig(de_ctx, "alert ip any any -> any any (fragoffset:5000; sid:2;)");
420 FAIL_IF_NULL(s);
421
423 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
424
425 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
426
427 FAIL_IF(PacketAlertCheck(p, 1) == 0);
429
430 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
432
433 FlowShutdown();
434
435 SCFree(p);
436 PASS;
437}
438
440{
441 UtRegisterTest("DetectFragOffsetParseTest01", DetectFragOffsetParseTest01);
442 UtRegisterTest("DetectFragOffsetParseTest02", DetectFragOffsetParseTest02);
443 UtRegisterTest("DetectFragOffsetParseTest03", DetectFragOffsetParseTest03);
444 UtRegisterTest("DetectFragOffsetMatchTest01", DetectFragOffsetMatchTest01);
445}
446#endif /* UNITTESTS */
#define IPV4_GET_RAW_FRAGOFFSET(ip4h)
#define IPV6_EXTHDR_GET_FH_OFFSET(p)
#define IPV6_EXTHDR_ISSET_FH(p)
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
int PrefilterSetupPacketHeader(DetectEngineCtx *de_ctx, SigGroupHead *sgh, int sm_type, SignatureMask mask, void(*Set)(PrefilterPacketHeaderValue *v, void *), bool(*Compare)(PrefilterPacketHeaderValue v, void *), void(*Match)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx))
@ DETECT_FRAGOFFSET
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
initialize thread specific detection engine context
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
void DetectFragOffsetFree(DetectEngineCtx *, void *)
this function will free memory associated with DetectFragOffsetData
void DetectFragOffsetRegister(void)
Registration function for fragoffset.
void DetectFragOffsetRegisterTests(void)
#define PARSE_REGEX
#define FRAG_MORE
#define FRAG_LESS
int SC_Pcre2SubstringGet(pcre2_match_data *match_data, uint32_t number, PCRE2_UCHAR **bufferptr, PCRE2_SIZE *bufflen)
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
SigMatch * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
SigTableElmt * sigmatch_table
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
#define DE_QUIET
Definition detect.h:330
#define SIG_FLAG_REQUIRE_PACKET
Definition detect.h:254
#define SIG_MASK_REQUIRE_REAL_PKT
Definition detect.h:316
@ DETECT_SM_LIST_MATCH
Definition detect.h:117
void FlowInitConfig(bool quiet)
initialize the configuration
Definition flow.c:547
void FlowShutdown(void)
shutdown the flow engine
Definition flow.c:691
#define FLOW_QUIET
Definition flow.h:43
DecodeThreadVars * dtv
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition decode.c:258
struct Thresholds ctx
char family
Definition decode.h:113
Structure to hold thread specific data for all decode modules.
Definition decode.h:963
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
PrefilterRuleStore pmq
Definition detect.h:1349
uint16_t ip_off
Definition decode-ipv4.h:77
Address src
Definition decode.h:505
Address dst
Definition decode.h:506
Container for matching data for a signature group.
Definition detect.h:1629
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * next
Definition detect.h:360
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
int(* SetupPrefilter)(DetectEngineCtx *de_ctx, struct SigGroupHead_ *sgh)
Definition detect.h:1444
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
bool(* SupportsPrefilter)(const Signature *s)
Definition detect.h:1443
struct SigMatch_ * smlists[DETECT_SM_LIST_MAX]
Definition detect.h:642
Signature container.
Definition detect.h:668
uint32_t flags
Definition detect.h:669
SignatureInitData * init_data
Definition detect.h:747
Per thread variable structure.
Definition threadvars.h:58
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition util-byte.c:337
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define unlikely(expr)
void UTHSetIPV4Hdr(Packet *p, IPV4Hdr *ip4h)
#define DEBUG_VALIDATE_BUG_ON(exp)