/src/suricata7/src/detect-icode.c
Line | Count | Source |
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 Gerardo Iglesias <iglesiasg@gmail.com> |
22 | | * |
23 | | * Implements icode keyword support |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "decode.h" |
28 | | |
29 | | #include "detect.h" |
30 | | #include "detect-parse.h" |
31 | | #include "detect-engine-prefilter-common.h" |
32 | | #include "detect-engine-uint.h" |
33 | | #include "detect-engine-build.h" |
34 | | |
35 | | #include "detect-icode.h" |
36 | | |
37 | | #include "util-byte.h" |
38 | | #include "util-unittest.h" |
39 | | #include "util-unittest-helper.h" |
40 | | #include "util-debug.h" |
41 | | |
42 | | /** |
43 | | *\brief Regex for parsing our icode options |
44 | | */ |
45 | | |
46 | | static int DetectICodeMatch(DetectEngineThreadCtx *, Packet *, |
47 | | const Signature *, const SigMatchCtx *); |
48 | | static int DetectICodeSetup(DetectEngineCtx *, Signature *, const char *); |
49 | | #ifdef UNITTESTS |
50 | | static void DetectICodeRegisterTests(void); |
51 | | #endif |
52 | | void DetectICodeFree(DetectEngineCtx *, void *); |
53 | | |
54 | | static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh); |
55 | | static bool PrefilterICodeIsPrefilterable(const Signature *s); |
56 | | |
57 | | /** |
58 | | * \brief Registration function for icode: keyword |
59 | | */ |
60 | | void DetectICodeRegister (void) |
61 | 75 | { |
62 | 75 | sigmatch_table[DETECT_ICODE].name = "icode"; |
63 | 75 | sigmatch_table[DETECT_ICODE].desc = "match on specific ICMP id-value"; |
64 | 75 | sigmatch_table[DETECT_ICODE].url = "/rules/header-keywords.html#icode"; |
65 | 75 | sigmatch_table[DETECT_ICODE].Match = DetectICodeMatch; |
66 | 75 | sigmatch_table[DETECT_ICODE].Setup = DetectICodeSetup; |
67 | 75 | sigmatch_table[DETECT_ICODE].Free = DetectICodeFree; |
68 | | #ifdef UNITTESTS |
69 | | sigmatch_table[DETECT_ICODE].RegisterTests = DetectICodeRegisterTests; |
70 | | #endif |
71 | 75 | sigmatch_table[DETECT_ICODE].SupportsPrefilter = PrefilterICodeIsPrefilterable; |
72 | 75 | sigmatch_table[DETECT_ICODE].SetupPrefilter = PrefilterSetupICode; |
73 | 75 | } |
74 | | |
75 | | /** |
76 | | * \brief This function is used to match icode rule option set on a packet with those passed via |
77 | | * icode: |
78 | | * |
79 | | * \param t pointer to thread vars |
80 | | * \param det_ctx pointer to the pattern matcher thread |
81 | | * \param p pointer to the current packet |
82 | | * \param ctx pointer to the sigmatch that we will cast into DetectU8Data |
83 | | * |
84 | | * \retval 0 no match |
85 | | * \retval 1 match |
86 | | */ |
87 | | static int DetectICodeMatch (DetectEngineThreadCtx *det_ctx, Packet *p, |
88 | | const Signature *s, const SigMatchCtx *ctx) |
89 | 5.19k | { |
90 | 5.19k | if (PKT_IS_PSEUDOPKT(p)) |
91 | 778 | return 0; |
92 | | |
93 | 4.41k | uint8_t picode; |
94 | 4.41k | if (PKT_IS_ICMPV4(p)) { |
95 | 2.67k | picode = ICMPV4_GET_CODE(p); |
96 | 2.67k | } else if (PKT_IS_ICMPV6(p)) { |
97 | 140 | picode = ICMPV6_GET_CODE(p); |
98 | 1.60k | } else { |
99 | | /* Packet not ICMPv4 nor ICMPv6 */ |
100 | 1.60k | return 0; |
101 | 1.60k | } |
102 | | |
103 | 2.81k | const DetectU8Data *icd = (const DetectU8Data *)ctx; |
104 | 2.81k | return DetectU8Match(picode, icd); |
105 | 4.41k | } |
106 | | |
107 | | /** |
108 | | * \brief this function is used to add the parsed icode data into the current signature |
109 | | * |
110 | | * \param de_ctx pointer to the Detection Engine Context |
111 | | * \param s pointer to the Current Signature |
112 | | * \param icodestr pointer to the user provided icode options |
113 | | * |
114 | | * \retval 0 on Success |
115 | | * \retval -1 on Failure |
116 | | */ |
117 | | static int DetectICodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *icodestr) |
118 | 2.88k | { |
119 | | |
120 | 2.88k | DetectU8Data *icd = NULL; |
121 | 2.88k | SigMatch *sm = NULL; |
122 | | |
123 | 2.88k | icd = DetectU8Parse(icodestr); |
124 | 2.88k | if (icd == NULL) |
125 | 180 | return -1; |
126 | | |
127 | 2.70k | sm = SigMatchAlloc(); |
128 | 2.70k | if (sm == NULL) goto error; |
129 | | |
130 | 2.70k | sm->type = DETECT_ICODE; |
131 | 2.70k | sm->ctx = (SigMatchCtx *)icd; |
132 | | |
133 | 2.70k | SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); |
134 | 2.70k | s->flags |= SIG_FLAG_REQUIRE_PACKET; |
135 | | |
136 | 2.70k | return 0; |
137 | | |
138 | 0 | error: |
139 | 0 | if (icd != NULL) |
140 | 0 | rs_detect_u8_free(icd); |
141 | 0 | if (sm != NULL) SCFree(sm); |
142 | 0 | return -1; |
143 | 2.70k | } |
144 | | |
145 | | /** |
146 | | * \brief this function will free memory associated with DetectU8Data |
147 | | * |
148 | | * \param ptr pointer to DetectU8Data |
149 | | */ |
150 | | void DetectICodeFree(DetectEngineCtx *de_ctx, void *ptr) |
151 | 3.47k | { |
152 | 3.47k | rs_detect_u8_free(ptr); |
153 | 3.47k | } |
154 | | |
155 | | /* prefilter code */ |
156 | | |
157 | | static void PrefilterPacketICodeMatch(DetectEngineThreadCtx *det_ctx, |
158 | | Packet *p, const void *pectx) |
159 | 0 | { |
160 | 0 | if (PKT_IS_PSEUDOPKT(p)) { |
161 | 0 | SCReturn; |
162 | 0 | } |
163 | | |
164 | 0 | uint8_t picode; |
165 | 0 | if (PKT_IS_ICMPV4(p)) { |
166 | 0 | picode = ICMPV4_GET_CODE(p); |
167 | 0 | } else if (PKT_IS_ICMPV6(p)) { |
168 | 0 | picode = ICMPV6_GET_CODE(p); |
169 | 0 | } else { |
170 | | /* Packet not ICMPv4 nor ICMPv6 */ |
171 | 0 | return; |
172 | 0 | } |
173 | | |
174 | 0 | const PrefilterPacketU8HashCtx *h = pectx; |
175 | 0 | const SigsArray *sa = h->array[picode]; |
176 | 0 | if (sa) { |
177 | 0 | PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt); |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh) |
182 | 0 | { |
183 | 0 | return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_ICODE, PrefilterPacketU8Set, |
184 | 0 | PrefilterPacketU8Compare, PrefilterPacketICodeMatch); |
185 | 0 | } |
186 | | |
187 | | static bool PrefilterICodeIsPrefilterable(const Signature *s) |
188 | 0 | { |
189 | 0 | const SigMatch *sm; |
190 | 0 | for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) { |
191 | 0 | switch (sm->type) { |
192 | 0 | case DETECT_ICODE: |
193 | 0 | return true; |
194 | 0 | } |
195 | 0 | } |
196 | 0 | return false; |
197 | 0 | } |
198 | | |
199 | | #ifdef UNITTESTS |
200 | | #include "detect-engine.h" |
201 | | #include "detect-engine-mpm.h" |
202 | | #include "detect-engine-alert.h" |
203 | | |
204 | | /** |
205 | | * \test DetectICodeParseTest01 is a test for setting a valid icode value |
206 | | */ |
207 | | static int DetectICodeParseTest01(void) |
208 | | { |
209 | | DetectU8Data *icd = DetectU8Parse("8"); |
210 | | FAIL_IF_NULL(icd); |
211 | | FAIL_IF_NOT(icd->arg1 == 8); |
212 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ); |
213 | | DetectICodeFree(NULL, icd); |
214 | | |
215 | | PASS; |
216 | | } |
217 | | |
218 | | /** |
219 | | * \test DetectICodeParseTest02 is a test for setting a valid icode value |
220 | | * with ">" operator |
221 | | */ |
222 | | static int DetectICodeParseTest02(void) |
223 | | { |
224 | | DetectU8Data *icd = DetectU8Parse(">8"); |
225 | | FAIL_IF_NULL(icd); |
226 | | FAIL_IF_NOT(icd->arg1 == 8); |
227 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_GT); |
228 | | DetectICodeFree(NULL, icd); |
229 | | |
230 | | PASS; |
231 | | } |
232 | | |
233 | | /** |
234 | | * \test DetectICodeParseTest03 is a test for setting a valid icode value |
235 | | * with "<" operator |
236 | | */ |
237 | | static int DetectICodeParseTest03(void) |
238 | | { |
239 | | DetectU8Data *icd = DetectU8Parse("<8"); |
240 | | FAIL_IF_NULL(icd); |
241 | | FAIL_IF_NOT(icd->arg1 == 8); |
242 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_LT); |
243 | | DetectICodeFree(NULL, icd); |
244 | | |
245 | | PASS; |
246 | | } |
247 | | |
248 | | /** |
249 | | * \test DetectICodeParseTest04 is a test for setting a valid icode value |
250 | | * with "<>" operator |
251 | | */ |
252 | | static int DetectICodeParseTest04(void) |
253 | | { |
254 | | DetectU8Data *icd = DetectU8Parse("8<>20"); |
255 | | FAIL_IF_NULL(icd); |
256 | | FAIL_IF_NOT(icd->arg1 == 8); |
257 | | FAIL_IF_NOT(icd->arg2 == 20); |
258 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_RA); |
259 | | DetectICodeFree(NULL, icd); |
260 | | |
261 | | PASS; |
262 | | } |
263 | | |
264 | | /** |
265 | | * \test DetectICodeParseTest05 is a test for setting a valid icode value |
266 | | * with spaces all around |
267 | | */ |
268 | | static int DetectICodeParseTest05(void) |
269 | | { |
270 | | DetectU8Data *icd = DetectU8Parse(" 8 "); |
271 | | FAIL_IF_NULL(icd); |
272 | | FAIL_IF_NOT(icd->arg1 == 8); |
273 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ); |
274 | | DetectICodeFree(NULL, icd); |
275 | | |
276 | | PASS; |
277 | | } |
278 | | |
279 | | /** |
280 | | * \test DetectICodeParseTest06 is a test for setting a valid icode value |
281 | | * with ">" operator and spaces all around |
282 | | */ |
283 | | static int DetectICodeParseTest06(void) |
284 | | { |
285 | | DetectU8Data *icd = DetectU8Parse(" > 8 "); |
286 | | FAIL_IF_NULL(icd); |
287 | | FAIL_IF_NOT(icd->arg1 == 8); |
288 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_GT); |
289 | | DetectICodeFree(NULL, icd); |
290 | | |
291 | | PASS; |
292 | | } |
293 | | |
294 | | /** |
295 | | * \test DetectICodeParseTest07 is a test for setting a valid icode value |
296 | | * with "<>" operator and spaces all around |
297 | | */ |
298 | | static int DetectICodeParseTest07(void) |
299 | | { |
300 | | DetectU8Data *icd = DetectU8Parse(" 8 <> 20 "); |
301 | | FAIL_IF_NULL(icd); |
302 | | FAIL_IF_NOT(icd->arg1 == 8); |
303 | | FAIL_IF_NOT(icd->arg2 == 20); |
304 | | FAIL_IF_NOT(icd->mode == DETECT_UINT_RA); |
305 | | DetectICodeFree(NULL, icd); |
306 | | |
307 | | PASS; |
308 | | } |
309 | | |
310 | | /** |
311 | | * \test DetectICodeParseTest08 is a test for setting an invalid icode value |
312 | | */ |
313 | | static int DetectICodeParseTest08(void) |
314 | | { |
315 | | DetectU8Data *icd = DetectU8Parse("> 8 <> 20"); |
316 | | FAIL_IF_NOT_NULL(icd); |
317 | | |
318 | | PASS; |
319 | | } |
320 | | |
321 | | /** |
322 | | * \test DetectICodeParseTest09 is a test for setting an invalid icode value |
323 | | * with "<<" operator |
324 | | */ |
325 | | static int DetectICodeParseTest09(void) |
326 | | { |
327 | | DetectU8Data *icd = DetectU8Parse("8<<20"); |
328 | | FAIL_IF_NOT_NULL(icd); |
329 | | |
330 | | PASS; |
331 | | } |
332 | | |
333 | | /** |
334 | | * \test DetectICodeMatchTest01 is a test for checking the working of icode |
335 | | * keyword by creating 5 rules and matching a crafted packet against |
336 | | * them. 4 out of 5 rules shall trigger. |
337 | | */ |
338 | | static int DetectICodeMatchTest01(void) |
339 | | { |
340 | | |
341 | | Packet *p = NULL; |
342 | | Signature *s = NULL; |
343 | | ThreadVars th_v; |
344 | | DetectEngineThreadCtx *det_ctx; |
345 | | |
346 | | memset(&th_v, 0, sizeof(th_v)); |
347 | | |
348 | | p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP); |
349 | | |
350 | | p->icmpv4h->code = 10; |
351 | | |
352 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
353 | | FAIL_IF_NULL(de_ctx); |
354 | | |
355 | | de_ctx->flags |= DE_QUIET; |
356 | | |
357 | | s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:10; sid:1;)"); |
358 | | FAIL_IF_NULL(s); |
359 | | |
360 | | s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:<15; sid:2;)"); |
361 | | FAIL_IF_NULL(s); |
362 | | |
363 | | s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:>20; sid:3;)"); |
364 | | FAIL_IF_NULL(s); |
365 | | |
366 | | s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:8<>20; sid:4;)"); |
367 | | FAIL_IF_NULL(s); |
368 | | |
369 | | s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:20<>8; sid:5;)"); |
370 | | FAIL_IF_NOT_NULL(s); |
371 | | |
372 | | SigGroupBuild(de_ctx); |
373 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
374 | | |
375 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
376 | | |
377 | | FAIL_IF(PacketAlertCheck(p, 1) == 0); |
378 | | FAIL_IF(PacketAlertCheck(p, 2) == 0); |
379 | | FAIL_IF(PacketAlertCheck(p, 3)); |
380 | | FAIL_IF(PacketAlertCheck(p, 4) == 0); |
381 | | |
382 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
383 | | DetectEngineCtxFree(de_ctx); |
384 | | |
385 | | UTHFreePackets(&p, 1); |
386 | | |
387 | | PASS; |
388 | | } |
389 | | |
390 | | /** |
391 | | * \brief this function registers unit tests for DetectICode |
392 | | */ |
393 | | void DetectICodeRegisterTests(void) |
394 | | { |
395 | | UtRegisterTest("DetectICodeParseTest01", DetectICodeParseTest01); |
396 | | UtRegisterTest("DetectICodeParseTest02", DetectICodeParseTest02); |
397 | | UtRegisterTest("DetectICodeParseTest03", DetectICodeParseTest03); |
398 | | UtRegisterTest("DetectICodeParseTest04", DetectICodeParseTest04); |
399 | | UtRegisterTest("DetectICodeParseTest05", DetectICodeParseTest05); |
400 | | UtRegisterTest("DetectICodeParseTest06", DetectICodeParseTest06); |
401 | | UtRegisterTest("DetectICodeParseTest07", DetectICodeParseTest07); |
402 | | UtRegisterTest("DetectICodeParseTest08", DetectICodeParseTest08); |
403 | | UtRegisterTest("DetectICodeParseTest09", DetectICodeParseTest09); |
404 | | UtRegisterTest("DetectICodeMatchTest01", DetectICodeMatchTest01); |
405 | | } |
406 | | #endif /* UNITTESTS */ |