/src/suricata7/src/detect-engine-proto.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2007-2022 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 Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Proto part of the detection engine. |
24 | | * |
25 | | * \todo move this out of the detection plugin structure |
26 | | */ |
27 | | |
28 | | #include "suricata-common.h" |
29 | | |
30 | | #include "decode.h" |
31 | | #include "detect.h" |
32 | | |
33 | | #include "app-layer-parser.h" |
34 | | |
35 | | #include "flow-util.h" |
36 | | #include "flow-var.h" |
37 | | |
38 | | #include "detect-engine-siggroup.h" |
39 | | #include "detect-engine-state.h" |
40 | | |
41 | | #include "util-cidr.h" |
42 | | #include "util-byte.h" |
43 | | #include "util-unittest.h" |
44 | | #include "util-unittest-helper.h" |
45 | | #include "util-debug.h" |
46 | | |
47 | | /** |
48 | | * \brief Parses a protocol sent as a string. |
49 | | * |
50 | | * \param dp Pointer to the DetectProto instance which will be updated with the |
51 | | * incoming protocol information. |
52 | | * \param str Pointer to the string containing the protocol name. |
53 | | * |
54 | | * \retval >=0 If proto is detected, -1 otherwise. |
55 | | */ |
56 | | int DetectProtoParse(DetectProto *dp, const char *str) |
57 | 2.87M | { |
58 | 2.87M | if (strcasecmp(str, "tcp") == 0) { |
59 | 796k | dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); |
60 | 796k | SCLogDebug("TCP protocol detected"); |
61 | 2.07M | } else if (strcasecmp(str, "tcp-pkt") == 0) { |
62 | 169k | dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); |
63 | 169k | SCLogDebug("TCP protocol detected, packets only"); |
64 | 169k | dp->flags |= DETECT_PROTO_ONLY_PKT; |
65 | 1.90M | } else if (strcasecmp(str, "tcp-stream") == 0) { |
66 | 20.7k | dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8); |
67 | 20.7k | SCLogDebug("TCP protocol detected, stream only"); |
68 | 20.7k | dp->flags |= DETECT_PROTO_ONLY_STREAM; |
69 | 1.88M | } else if (strcasecmp(str, "udp") == 0) { |
70 | 69.7k | dp->proto[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8); |
71 | 69.7k | SCLogDebug("UDP protocol detected"); |
72 | 1.81M | } else if (strcasecmp(str, "icmpv4") == 0) { |
73 | 567 | dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8); |
74 | 567 | SCLogDebug("ICMPv4 protocol detected"); |
75 | 1.81M | } else if (strcasecmp(str, "icmpv6") == 0) { |
76 | 281 | dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8); |
77 | 281 | SCLogDebug("ICMPv6 protocol detected"); |
78 | 1.81M | } else if (strcasecmp(str, "icmp") == 0) { |
79 | 17.2k | dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8); |
80 | 17.2k | dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8); |
81 | 17.2k | SCLogDebug("ICMP protocol detected, sig applies both to ICMPv4 and ICMPv6"); |
82 | 1.79M | } else if (strcasecmp(str, "sctp") == 0) { |
83 | 24 | dp->proto[IPPROTO_SCTP / 8] |= 1 << (IPPROTO_SCTP % 8); |
84 | 24 | SCLogDebug("SCTP protocol detected"); |
85 | 1.79M | } else if (strcasecmp(str,"ipv4") == 0 || |
86 | 1.79M | strcasecmp(str,"ip4") == 0 ) { |
87 | 21.9k | dp->flags |= (DETECT_PROTO_IPV4 | DETECT_PROTO_ANY); |
88 | 21.9k | memset(dp->proto, 0xff, sizeof(dp->proto)); |
89 | 21.9k | SCLogDebug("IPv4 protocol detected"); |
90 | 1.77M | } else if (strcasecmp(str,"ipv6") == 0 || |
91 | 1.77M | strcasecmp(str,"ip6") == 0 ) { |
92 | 11.1k | dp->flags |= (DETECT_PROTO_IPV6 | DETECT_PROTO_ANY); |
93 | 11.1k | memset(dp->proto, 0xff, sizeof(dp->proto)); |
94 | 11.1k | SCLogDebug("IPv6 protocol detected"); |
95 | 1.76M | } else if (strcasecmp(str,"ip") == 0 || |
96 | 1.76M | strcasecmp(str,"pkthdr") == 0) { |
97 | | /* Proto "ip" is treated as an "any" */ |
98 | 618k | dp->flags |= DETECT_PROTO_ANY; |
99 | 618k | memset(dp->proto, 0xff, sizeof(dp->proto)); |
100 | 618k | SCLogDebug("IP protocol detected"); |
101 | 1.14M | } else { |
102 | 1.14M | goto error; |
103 | | |
104 | | /** \todo are numeric protocols even valid? */ |
105 | | #if 0 |
106 | | uint8_t proto_u8; /* Used to avoid sign extension */ |
107 | | |
108 | | /* Extract out a 0-256 value with validation checks */ |
109 | | if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) { |
110 | | // XXX |
111 | | SCLogDebug("DetectProtoParse: Error in extracting byte string"); |
112 | | goto error; |
113 | | } |
114 | | proto = (int)proto_u8; |
115 | | |
116 | | /* Proto 0 is the same as "ip" above */ |
117 | | if (proto == IPPROTO_IP) { |
118 | | dp->flags |= DETECT_PROTO_ANY; |
119 | | } else { |
120 | | dp->proto[proto / 8] |= 1<<(proto % 8); |
121 | | } |
122 | | #endif |
123 | 1.14M | } |
124 | | |
125 | 1.72M | return 0; |
126 | 1.14M | error: |
127 | 1.14M | return -1; |
128 | 2.87M | } |
129 | | |
130 | | /** \brief see if a DetectProto contains a certain proto |
131 | | * \param dp detect proto to inspect |
132 | | * \param proto protocol (such as IPPROTO_TCP) to look for |
133 | | * \retval 0 protocol not in the set |
134 | | * \retval 1 protocol is in the set */ |
135 | | int DetectProtoContainsProto(const DetectProto *dp, int proto) |
136 | 4.76M | { |
137 | 4.76M | if (dp->flags & DETECT_PROTO_ANY) |
138 | 2.33M | return 1; |
139 | | |
140 | 2.42M | if (dp->proto[proto / 8] & (1<<(proto % 8))) |
141 | 2.36M | return 1; |
142 | | |
143 | 65.2k | return 0; |
144 | 2.42M | } |
145 | | |
146 | | /* TESTS */ |
147 | | |
148 | | #ifdef UNITTESTS |
149 | | #include "detect-engine.h" |
150 | | #include "detect-parse.h" |
151 | | #include "detect-engine-mpm.h" |
152 | | /** |
153 | | * \brief this function is used to initialize the detection engine context and |
154 | | * setup the signature with passed values. |
155 | | */ |
156 | | static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig, |
157 | | DetectProto *dp, const char *str) |
158 | | { |
159 | | char fullstr[1024]; |
160 | | int result = 0; |
161 | | |
162 | | *de_ctx = NULL; |
163 | | *sig = NULL; |
164 | | |
165 | | if (snprintf(fullstr, 1024, "alert %s any any -> any any (msg:\"DetectProto" |
166 | | " test\"; sid:1;)", str) >= 1024) |
167 | | { |
168 | | goto end; |
169 | | } |
170 | | |
171 | | *de_ctx = DetectEngineCtxInit(); |
172 | | if (*de_ctx == NULL) { |
173 | | goto end; |
174 | | } |
175 | | |
176 | | (*de_ctx)->flags |= DE_QUIET; |
177 | | |
178 | | (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr); |
179 | | if ((*de_ctx)->sig_list == NULL) { |
180 | | goto end; |
181 | | } |
182 | | |
183 | | *sig = (*de_ctx)->sig_list; |
184 | | |
185 | | if (DetectProtoParse(dp, str) < 0) |
186 | | goto end; |
187 | | |
188 | | result = 1; |
189 | | |
190 | | end: |
191 | | return result; |
192 | | } |
193 | | |
194 | | /** |
195 | | * \test ProtoTestParse01 is a test to make sure that we parse the |
196 | | * protocol correctly, when given valid proto option. |
197 | | */ |
198 | | static int ProtoTestParse01 (void) |
199 | | { |
200 | | DetectProto dp; |
201 | | memset(&dp,0,sizeof(DetectProto)); |
202 | | |
203 | | int r = DetectProtoParse(&dp, "6"); |
204 | | |
205 | | FAIL_IF_NOT(r < 0); |
206 | | |
207 | | PASS; |
208 | | } |
209 | | /** |
210 | | * \test ProtoTestParse02 is a test to make sure that we parse the |
211 | | * protocol correctly, when given "tcp" as proto option. |
212 | | */ |
213 | | static int ProtoTestParse02 (void) |
214 | | { |
215 | | DetectProto dp; |
216 | | memset(&dp,0,sizeof(DetectProto)); |
217 | | |
218 | | int r = DetectProtoParse(&dp, "tcp"); |
219 | | |
220 | | FAIL_IF_NOT(r >= 0); |
221 | | FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8))); |
222 | | |
223 | | PASS; |
224 | | } |
225 | | /** |
226 | | * \test ProtoTestParse03 is a test to make sure that we parse the |
227 | | * protocol correctly, when given "ip" as proto option. |
228 | | */ |
229 | | static int ProtoTestParse03 (void) |
230 | | { |
231 | | DetectProto dp; |
232 | | memset(&dp,0,sizeof(DetectProto)); |
233 | | |
234 | | int r = DetectProtoParse(&dp, "ip"); |
235 | | |
236 | | FAIL_IF_NOT(r >= 0); |
237 | | FAIL_IF_NOT(dp.flags & DETECT_PROTO_ANY); |
238 | | |
239 | | PASS; |
240 | | } |
241 | | |
242 | | /** |
243 | | * \test ProtoTestParse04 is a test to make sure that we do not parse the |
244 | | * protocol, when given an invalid proto option. |
245 | | */ |
246 | | static int ProtoTestParse04 (void) |
247 | | { |
248 | | DetectProto dp; |
249 | | memset(&dp,0,sizeof(DetectProto)); |
250 | | |
251 | | /* Check for a bad number */ |
252 | | int r = DetectProtoParse(&dp, "4242"); |
253 | | |
254 | | FAIL_IF_NOT(r < 0); |
255 | | |
256 | | PASS; |
257 | | } |
258 | | |
259 | | /** |
260 | | * \test ProtoTestParse05 is a test to make sure that we do not parse the |
261 | | * protocol, when given an invalid proto option. |
262 | | */ |
263 | | static int ProtoTestParse05 (void) |
264 | | { |
265 | | DetectProto dp; |
266 | | memset(&dp,0,sizeof(DetectProto)); |
267 | | |
268 | | /* Check for a bad string */ |
269 | | int r = DetectProtoParse(&dp, "tcp/udp"); |
270 | | |
271 | | FAIL_IF_NOT(r < 0); |
272 | | |
273 | | PASS; |
274 | | } |
275 | | |
276 | | /** |
277 | | * \test make sure that we properly parse tcp-pkt |
278 | | */ |
279 | | static int ProtoTestParse06 (void) |
280 | | { |
281 | | DetectProto dp; |
282 | | memset(&dp,0,sizeof(DetectProto)); |
283 | | |
284 | | /* Check for a bad string */ |
285 | | int r = DetectProtoParse(&dp, "tcp-pkt"); |
286 | | |
287 | | FAIL_IF(r < 0); |
288 | | FAIL_IF_NOT(dp.flags & DETECT_PROTO_ONLY_PKT); |
289 | | |
290 | | PASS; |
291 | | } |
292 | | |
293 | | /** |
294 | | * \test make sure that we properly parse tcp-stream |
295 | | */ |
296 | | static int ProtoTestParse07 (void) |
297 | | { |
298 | | DetectProto dp; |
299 | | memset(&dp,0,sizeof(DetectProto)); |
300 | | |
301 | | /* Check for a bad string */ |
302 | | int r = DetectProtoParse(&dp, "tcp-stream"); |
303 | | |
304 | | FAIL_IF(r < 0); |
305 | | FAIL_IF_NOT(dp.flags & DETECT_PROTO_ONLY_STREAM); |
306 | | |
307 | | PASS; |
308 | | } |
309 | | |
310 | | /** |
311 | | * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in |
312 | | * signature. |
313 | | */ |
314 | | static int DetectProtoTestSetup01(void) |
315 | | { |
316 | | DetectProto dp; |
317 | | Signature *sig = NULL; |
318 | | DetectEngineCtx *de_ctx = NULL; |
319 | | int i; |
320 | | |
321 | | memset(&dp, 0, sizeof(dp)); |
322 | | |
323 | | FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp")); |
324 | | |
325 | | /* The signature proto should be TCP */ |
326 | | FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8))); |
327 | | |
328 | | for (i = 2; i < 256 / 8; i++) { |
329 | | FAIL_IF(sig->proto.proto[i] != 0); |
330 | | } |
331 | | |
332 | | DetectEngineCtxFree(de_ctx); |
333 | | |
334 | | PASS; |
335 | | } |
336 | | |
337 | | /** |
338 | | * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6 |
339 | | * protocol setting up in signature. |
340 | | */ |
341 | | static int DetectProtoTestSetup02(void) |
342 | | { |
343 | | DetectProto dp; |
344 | | Signature *sig_icmpv4 = NULL; |
345 | | Signature *sig_icmpv6 = NULL; |
346 | | Signature *sig_icmp = NULL; |
347 | | DetectEngineCtx *de_ctx = NULL; |
348 | | |
349 | | memset(&dp, 0, sizeof(dp)); |
350 | | |
351 | | FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0); |
352 | | FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0); |
353 | | FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0); |
354 | | |
355 | | FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8))); |
356 | | FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8))); |
357 | | |
358 | | FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8))); |
359 | | FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8))); |
360 | | |
361 | | DetectEngineCtxFree(de_ctx); |
362 | | |
363 | | PASS; |
364 | | } |
365 | | |
366 | | /** |
367 | | * \test signature parsing with tcp-pkt and tcp-stream |
368 | | */ |
369 | | |
370 | | static int DetectProtoTestSig01(void) |
371 | | { |
372 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
373 | | FAIL_IF_NULL(de_ctx); |
374 | | |
375 | | de_ctx->flags |= DE_QUIET; |
376 | | |
377 | | Signature *s = DetectEngineAppendSig( |
378 | | de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)"); |
379 | | FAIL_IF_NULL(s); |
380 | | |
381 | | s = DetectEngineAppendSig(de_ctx, |
382 | | "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)"); |
383 | | FAIL_IF_NULL(s); |
384 | | |
385 | | DetectEngineCtxFree(de_ctx); |
386 | | |
387 | | PASS; |
388 | | } |
389 | | #endif /* UNITTESTS */ |
390 | | |
391 | | /** |
392 | | * \brief this function registers unit tests for DetectProto |
393 | | */ |
394 | | void DetectProtoTests(void) |
395 | 0 | { |
396 | | #ifdef UNITTESTS |
397 | | UtRegisterTest("ProtoTestParse01", ProtoTestParse01); |
398 | | UtRegisterTest("ProtoTestParse02", ProtoTestParse02); |
399 | | UtRegisterTest("ProtoTestParse03", ProtoTestParse03); |
400 | | UtRegisterTest("ProtoTestParse04", ProtoTestParse04); |
401 | | UtRegisterTest("ProtoTestParse05", ProtoTestParse05); |
402 | | UtRegisterTest("ProtoTestParse06", ProtoTestParse06); |
403 | | UtRegisterTest("ProtoTestParse07", ProtoTestParse07); |
404 | | |
405 | | UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01); |
406 | | UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02); |
407 | | |
408 | | UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01); |
409 | | #endif /* UNITTESTS */ |
410 | 0 | } |
411 | | |