/src/suricata7/src/app-layer-modbus.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2014 ANSSI |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * 1. Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * 3. The name of the author may not be used to endorse or promote products |
14 | | * derived from this software without specific prior written permission. |
15 | | * |
16 | | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
17 | | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
18 | | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
19 | | * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
20 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
22 | | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
23 | | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
24 | | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
25 | | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | | */ |
27 | | |
28 | | /** |
29 | | * \file |
30 | | * |
31 | | * \author David DIALLO <diallo@et.esiea.fr> |
32 | | * |
33 | | * App-layer parser for Modbus protocol |
34 | | * |
35 | | */ |
36 | | |
37 | | #include "suricata-common.h" |
38 | | |
39 | | #include "util-debug.h" |
40 | | |
41 | | #include "app-layer-parser.h" |
42 | | #include "app-layer-modbus.h" |
43 | | |
44 | | void ModbusParserRegisterTests(void); |
45 | | |
46 | | /** |
47 | | * \brief Function to register the Modbus protocol parser |
48 | | */ |
49 | | void RegisterModbusParsers(void) |
50 | 74 | { |
51 | 74 | rs_modbus_register_parser(); |
52 | | #ifdef UNITTESTS |
53 | | AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_MODBUS, ModbusParserRegisterTests); |
54 | | #endif |
55 | | |
56 | 74 | SCReturn; |
57 | 74 | } |
58 | | |
59 | | /* UNITTESTS */ |
60 | | #ifdef UNITTESTS |
61 | | #include "detect.h" |
62 | | #include "detect-engine.h" |
63 | | #include "detect-parse.h" |
64 | | #include "detect-engine-build.h" |
65 | | #include "detect-engine-alert.h" |
66 | | |
67 | | #include "flow-util.h" |
68 | | |
69 | | #include "util-unittest.h" |
70 | | #include "util-unittest-helper.h" |
71 | | |
72 | | #include "stream-tcp.h" |
73 | | #include "stream-tcp-private.h" |
74 | | |
75 | | #include "rust.h" |
76 | | |
77 | | /* Modbus default stream reassembly depth */ |
78 | | #define MODBUS_CONFIG_DEFAULT_STREAM_DEPTH 0 |
79 | | |
80 | | /* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */ |
81 | | static uint8_t invalidFunctionCode[] = { |
82 | | /* Transaction ID */ 0x00, 0x00, |
83 | | /* Protocol ID */ 0x00, 0x00, |
84 | | /* Length */ 0x00, 0x02, |
85 | | /* Unit ID */ 0x00, |
86 | | /* Function code */ 0x00 |
87 | | }; |
88 | | |
89 | | /* Modbus Application Protocol Specification V1.1b3 6.1: Read Coils */ |
90 | | /* Example of a request to read discrete outputs 20-38 */ |
91 | | static uint8_t readCoilsReq[] = {/* Transaction ID */ 0x00, 0x00, |
92 | | /* Protocol ID */ 0x00, 0x00, |
93 | | /* Length */ 0x00, 0x06, |
94 | | /* Unit ID */ 0x00, |
95 | | /* Function code */ 0x01, |
96 | | /* Starting Address */ 0x78, 0x90, |
97 | | /* Quantity of coils */ 0x00, 0x13 }; |
98 | | |
99 | | static uint8_t readCoilsRsp[] = {/* Transaction ID */ 0x00, 0x00, |
100 | | /* Protocol ID */ 0x00, 0x00, |
101 | | /* Length */ 0x00, 0x06, |
102 | | /* Unit ID */ 0x00, |
103 | | /* Function code */ 0x01, |
104 | | /* Byte count */ 0x03, |
105 | | /* Coil Status */ 0xCD, 0x6B, 0x05 }; |
106 | | |
107 | | static uint8_t readCoilsErrorRsp[] = { |
108 | | /* Transaction ID */ 0x00, 0x00, |
109 | | /* Protocol ID */ 0x00, 0x00, |
110 | | /* Length */ 0x00, 0x03, |
111 | | /* Unit ID */ 0x00, |
112 | | /* Function code */ 0x81, |
113 | | /* Invalid Exception code: should trigger the InvalidExceptionCode ModbusEvent */ |
114 | | 0xFF |
115 | | }; |
116 | | |
117 | | /* Modbus Application Protocol Specification V1.1b3 6.6: Write Single register */ |
118 | | /* Example of a request to write register 2 to 00 03 hex */ |
119 | | static uint8_t writeSingleRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A, |
120 | | /* Protocol ID */ 0x00, 0x00, |
121 | | /* Length */ 0x00, 0x06, |
122 | | /* Unit ID */ 0x00, |
123 | | /* Function code */ 0x06, |
124 | | /* Register Address */ 0x00, 0x01, |
125 | | /* Register Value */ 0x00, 0x03}; |
126 | | |
127 | | static uint8_t invalidWriteSingleRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A, |
128 | | /* Protocol ID */ 0x00, 0x00, |
129 | | /* Length */ 0x00, 0x04, |
130 | | /* Unit ID */ 0x00, |
131 | | /* Function code */ 0x06, |
132 | | /* Register Address */ 0x00, 0x01}; |
133 | | |
134 | | static uint8_t writeSingleRegisterRsp[] = {/* Transaction ID */ 0x00, 0x0A, |
135 | | /* Protocol ID */ 0x00, 0x00, |
136 | | /* Length */ 0x00, 0x06, |
137 | | /* Unit ID */ 0x00, |
138 | | /* Function code */ 0x06, |
139 | | /* Register Address */ 0x00, 0x01, |
140 | | /* Register Value */ 0x00, 0x03}; |
141 | | |
142 | | /* Modbus Application Protocol Specification V1.1b3 6.12: Write Multiple registers */ |
143 | | /* Example of a request to write two registers starting at 2 to 00 0A and 01 02 hex */ |
144 | | static uint8_t writeMultipleRegistersReq[] = {/* Transaction ID */ 0x00, 0x0A, |
145 | | /* Protocol ID */ 0x00, 0x00, |
146 | | /* Length */ 0x00, 0x0B, |
147 | | /* Unit ID */ 0x00, |
148 | | /* Function code */ 0x10, |
149 | | /* Starting Address */ 0x00, 0x01, |
150 | | /* Quantity of Registers */ 0x00, 0x02, |
151 | | /* Byte count */ 0x04, |
152 | | /* Registers Value */ 0x00, 0x0A, |
153 | | 0x01, 0x02}; |
154 | | |
155 | | static uint8_t writeMultipleRegistersRsp[] = {/* Transaction ID */ 0x00, 0x0A, |
156 | | /* Protocol ID */ 0x00, 0x00, |
157 | | /* Length */ 0x00, 0x06, |
158 | | /* Unit ID */ 0x00, |
159 | | /* Function code */ 0x10, |
160 | | /* Starting Address */ 0x00, 0x01, |
161 | | /* Quantity of Registers */ 0x00, 0x02}; |
162 | | |
163 | | /* Modbus Application Protocol Specification V1.1b3 6.16: Mask Write Register */ |
164 | | /* Example of a request to mask write to register 5 */ |
165 | | static uint8_t maskWriteRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A, |
166 | | /* Protocol ID */ 0x00, 0x00, |
167 | | /* Length */ 0x00, 0x08, |
168 | | /* Unit ID */ 0x00, |
169 | | /* Function code */ 0x16, |
170 | | /* Reference Address */ 0x00, 0x04, |
171 | | /* And_Mask */ 0x00, 0xF2, |
172 | | /* Or_Mask */ 0x00, 0x25}; |
173 | | |
174 | | static uint8_t invalidMaskWriteRegisterReq[] = {/* Transaction ID */ 0x00, 0x0A, |
175 | | /* Protocol ID */ 0x00, 0x00, |
176 | | /* Length */ 0x00, 0x06, |
177 | | /* Unit ID */ 0x00, |
178 | | /* Function code */ 0x16, |
179 | | /* Reference Address */ 0x00, 0x04, |
180 | | /* And_Mask */ 0x00, 0xF2}; |
181 | | |
182 | | static uint8_t maskWriteRegisterRsp[] = {/* Transaction ID */ 0x00, 0x0A, |
183 | | /* Protocol ID */ 0x00, 0x00, |
184 | | /* Length */ 0x00, 0x08, |
185 | | /* Unit ID */ 0x00, |
186 | | /* Function code */ 0x16, |
187 | | /* Reference Address */ 0x00, 0x04, |
188 | | /* And_Mask */ 0x00, 0xF2, |
189 | | /* Or_Mask */ 0x00, 0x25}; |
190 | | |
191 | | /* Modbus Application Protocol Specification V1.1b3 6.17: Read/Write Multiple registers */ |
192 | | /* Example of a request to read six registers starting at register 4, */ |
193 | | /* and to write three registers starting at register 15 */ |
194 | | static uint8_t readWriteMultipleRegistersReq[] = {/* Transaction ID */ 0x12, 0x34, |
195 | | /* Protocol ID */ 0x00, 0x00, |
196 | | /* Length */ 0x00, 0x11, |
197 | | /* Unit ID */ 0x00, |
198 | | /* Function code */ 0x17, |
199 | | /* Read Starting Address */ 0x00, 0x03, |
200 | | /* Quantity to Read */ 0x00, 0x06, |
201 | | /* Write Starting Address */ 0x00, 0x0E, |
202 | | /* Quantity to Write */ 0x00, 0x03, |
203 | | /* Write Byte count */ 0x06, |
204 | | /* Write Registers Value */ 0x12, 0x34, |
205 | | 0x56, 0x78, |
206 | | 0x9A, 0xBC}; |
207 | | |
208 | | /* Mismatch value in Byte count 0x0B instead of 0x0C */ |
209 | | static uint8_t readWriteMultipleRegistersRsp[] = {/* Transaction ID */ 0x12, 0x34, |
210 | | /* Protocol ID */ 0x00, 0x00, |
211 | | /* Length */ 0x00, 0x0E, |
212 | | /* Unit ID */ 0x00, |
213 | | /* Function code */ 0x17, |
214 | | /* Byte count */ 0x0B, |
215 | | /* Read Registers Value */ 0x00, 0xFE, |
216 | | 0x0A, 0xCD, |
217 | | 0x00, 0x01, |
218 | | 0x00, 0x03, |
219 | | 0x00, 0x0D, |
220 | | 0x00}; |
221 | | |
222 | | /* Modbus Application Protocol Specification V1.1b3 6.8.1: 04 Force Listen Only Mode */ |
223 | | /* Example of a request to to remote device to its Listen Only Mode for Modbus Communications. */ |
224 | | static uint8_t forceListenOnlyMode[] = {/* Transaction ID */ 0x0A, 0x00, |
225 | | /* Protocol ID */ 0x00, 0x00, |
226 | | /* Length */ 0x00, 0x06, |
227 | | /* Unit ID */ 0x00, |
228 | | /* Function code */ 0x08, |
229 | | /* Sub-function code */ 0x00, 0x04, |
230 | | /* Data */ 0x00, 0x00}; |
231 | | |
232 | | static uint8_t invalidProtocolIdReq[] = {/* Transaction ID */ 0x00, 0x00, |
233 | | /* Protocol ID */ 0x00, 0x01, |
234 | | /* Length */ 0x00, 0x06, |
235 | | /* Unit ID */ 0x00, |
236 | | /* Function code */ 0x01, |
237 | | /* Starting Address */ 0x78, 0x90, |
238 | | /* Quantity of coils */ 0x00, 0x13 }; |
239 | | |
240 | | static uint8_t invalidLengthWriteMultipleRegistersReq[] = { |
241 | | /* Transaction ID */ 0x00, 0x0A, |
242 | | /* Protocol ID */ 0x00, 0x00, |
243 | | /* Length */ 0x00, 0x09, |
244 | | /* Unit ID */ 0x00, |
245 | | /* Function code */ 0x10, |
246 | | /* Starting Address */ 0x00, 0x01, |
247 | | /* Quantity of Registers */ 0x00, 0x02, |
248 | | /* Byte count */ 0x04, |
249 | | /* Registers Value */ 0x00, 0x0A, |
250 | | 0x01, 0x02}; |
251 | | |
252 | | static uint8_t exceededLengthWriteMultipleRegistersReq[] = { |
253 | | /* Transaction ID */ 0x00, 0x0A, |
254 | | /* Protocol ID */ 0x00, 0x00, |
255 | | /* Length */ 0xff, 0xfa, |
256 | | /* Unit ID */ 0x00, |
257 | | /* Function code */ 0x10, |
258 | | /* Starting Address */ 0x00, 0x01, |
259 | | /* Quantity of Registers */ 0x7f, 0xf9, |
260 | | /* Byte count */ 0xff}; |
261 | | |
262 | | static uint8_t invalidLengthPDUWriteMultipleRegistersReq[] = { |
263 | | /* Transaction ID */ 0x00, 0x0A, |
264 | | /* Protocol ID */ 0x00, 0x00, |
265 | | /* Length */ 0x00, 0x02, |
266 | | /* Unit ID */ 0x00, |
267 | | /* Function code */ 0x10}; |
268 | | |
269 | | /** \test Send Modbus Read Coils request/response. */ |
270 | | static int ModbusParserTest01(void) { |
271 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
272 | | Flow f; |
273 | | TcpSession ssn; |
274 | | |
275 | | FAIL_IF_NULL(alp_tctx); |
276 | | |
277 | | memset(&f, 0, sizeof(f)); |
278 | | memset(&ssn, 0, sizeof(ssn)); |
279 | | |
280 | | FLOW_INITIALIZE(&f); |
281 | | f.protoctx = (void *)&ssn; |
282 | | f.proto = IPPROTO_TCP; |
283 | | f.alproto = ALPROTO_MODBUS; |
284 | | |
285 | | StreamTcpInitConfig(true); |
286 | | |
287 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
288 | | STREAM_TOSERVER, readCoilsReq, |
289 | | sizeof(readCoilsReq)); |
290 | | FAIL_IF_NOT(r == 0); |
291 | | |
292 | | ModbusState *modbus_state = f.alstate; |
293 | | FAIL_IF_NULL(modbus_state); |
294 | | |
295 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
296 | | FAIL_IF_NULL(request._0); |
297 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1); |
298 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890); |
299 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19); |
300 | | |
301 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
302 | | STREAM_TOCLIENT, readCoilsRsp, |
303 | | sizeof(readCoilsRsp)); |
304 | | FAIL_IF_NOT(r == 0); |
305 | | |
306 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
307 | | |
308 | | AppLayerParserThreadCtxFree(alp_tctx); |
309 | | StreamTcpFreeConfig(true); |
310 | | FLOW_DESTROY(&f); |
311 | | PASS; |
312 | | } |
313 | | |
314 | | /** \test Send Modbus Write Multiple registers request/response. */ |
315 | | static int ModbusParserTest02(void) { |
316 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
317 | | Flow f; |
318 | | TcpSession ssn; |
319 | | |
320 | | FAIL_IF_NULL(alp_tctx); |
321 | | |
322 | | memset(&f, 0, sizeof(f)); |
323 | | memset(&ssn, 0, sizeof(ssn)); |
324 | | |
325 | | FLOW_INITIALIZE(&f); |
326 | | f.protoctx = (void *)&ssn; |
327 | | f.proto = IPPROTO_TCP; |
328 | | f.alproto = ALPROTO_MODBUS; |
329 | | |
330 | | StreamTcpInitConfig(true); |
331 | | |
332 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
333 | | STREAM_TOSERVER, writeMultipleRegistersReq, |
334 | | sizeof(writeMultipleRegistersReq)); |
335 | | FAIL_IF_NOT(r == 0); |
336 | | |
337 | | ModbusState *modbus_state = f.alstate; |
338 | | FAIL_IF_NULL(modbus_state); |
339 | | |
340 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
341 | | FAIL_IF_NULL(request._0); |
342 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 16); |
343 | | FAIL_IF_NOT(rs_modbus_message_get_write_multreq_address(&request) == 0x01); |
344 | | FAIL_IF_NOT(rs_modbus_message_get_write_multreq_quantity(&request) == 2); |
345 | | |
346 | | size_t data_len; |
347 | | const uint8_t *data = rs_modbus_message_get_write_multreq_data(&request, &data_len); |
348 | | FAIL_IF_NOT(data_len == 4); |
349 | | FAIL_IF_NOT(data[0] == 0x00); |
350 | | FAIL_IF_NOT(data[1] == 0x0A); |
351 | | FAIL_IF_NOT(data[2] == 0x01); |
352 | | FAIL_IF_NOT(data[3] == 0x02); |
353 | | |
354 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
355 | | STREAM_TOCLIENT, writeMultipleRegistersRsp, |
356 | | sizeof(writeMultipleRegistersRsp)); |
357 | | FAIL_IF_NOT(r == 0); |
358 | | |
359 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
360 | | |
361 | | AppLayerParserThreadCtxFree(alp_tctx); |
362 | | StreamTcpFreeConfig(true); |
363 | | FLOW_DESTROY(&f); |
364 | | PASS; |
365 | | } |
366 | | |
367 | | /** \test Send Modbus Read/Write Multiple registers request/response with mismatch value. */ |
368 | | static int ModbusParserTest03(void) { |
369 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
370 | | DetectEngineThreadCtx *det_ctx = NULL; |
371 | | Flow f; |
372 | | Packet *p = NULL; |
373 | | Signature *s = NULL; |
374 | | TcpSession ssn; |
375 | | ThreadVars tv; |
376 | | |
377 | | FAIL_IF_NULL(alp_tctx); |
378 | | |
379 | | memset(&tv, 0, sizeof(ThreadVars)); |
380 | | memset(&f, 0, sizeof(Flow)); |
381 | | memset(&ssn, 0, sizeof(TcpSession)); |
382 | | |
383 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
384 | | |
385 | | FLOW_INITIALIZE(&f); |
386 | | f.alproto = ALPROTO_MODBUS; |
387 | | f.protoctx = (void *)&ssn; |
388 | | f.proto = IPPROTO_TCP; |
389 | | f.alproto = ALPROTO_MODBUS; |
390 | | f.flags |= FLOW_IPV4; |
391 | | |
392 | | p->flow = &f; |
393 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
394 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
395 | | |
396 | | StreamTcpInitConfig(true); |
397 | | |
398 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
399 | | FAIL_IF_NULL(de_ctx); |
400 | | |
401 | | de_ctx->flags |= DE_QUIET; |
402 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
403 | | "(msg:\"Modbus Data mismatch\"; " |
404 | | "app-layer-event: " |
405 | | "modbus.value_mismatch; " |
406 | | "sid:1;)"); |
407 | | FAIL_IF_NULL(s); |
408 | | |
409 | | SigGroupBuild(de_ctx); |
410 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
411 | | |
412 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
413 | | STREAM_TOSERVER, |
414 | | readWriteMultipleRegistersReq, |
415 | | sizeof(readWriteMultipleRegistersReq)); |
416 | | FAIL_IF_NOT(r == 0); |
417 | | |
418 | | ModbusState *modbus_state = f.alstate; |
419 | | FAIL_IF_NULL(modbus_state); |
420 | | |
421 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
422 | | FAIL_IF_NULL(request._0); |
423 | | |
424 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 23); |
425 | | FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_read_address(&request) == 0x03); |
426 | | FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_read_quantity(&request) == 6); |
427 | | FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_write_address(&request) == 0x0E); |
428 | | FAIL_IF_NOT(rs_modbus_message_get_rw_multreq_write_quantity(&request) == 3); |
429 | | |
430 | | size_t data_len; |
431 | | uint8_t const *data = rs_modbus_message_get_rw_multreq_write_data(&request, &data_len); |
432 | | FAIL_IF_NOT(data_len == 6); |
433 | | FAIL_IF_NOT(data[0] == 0x12); |
434 | | FAIL_IF_NOT(data[1] == 0x34); |
435 | | FAIL_IF_NOT(data[2] == 0x56); |
436 | | FAIL_IF_NOT(data[3] == 0x78); |
437 | | FAIL_IF_NOT(data[4] == 0x9A); |
438 | | FAIL_IF_NOT(data[5] == 0xBC); |
439 | | |
440 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
441 | | STREAM_TOCLIENT, readWriteMultipleRegistersRsp, |
442 | | sizeof(readWriteMultipleRegistersRsp)); |
443 | | FAIL_IF_NOT(r == 0); |
444 | | |
445 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
446 | | |
447 | | /* do detect */ |
448 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
449 | | |
450 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
451 | | |
452 | | SigGroupCleanup(de_ctx); |
453 | | SigCleanSignatures(de_ctx); |
454 | | |
455 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
456 | | DetectEngineCtxFree(de_ctx); |
457 | | |
458 | | AppLayerParserThreadCtxFree(alp_tctx); |
459 | | StreamTcpFreeConfig(true); |
460 | | FLOW_DESTROY(&f); |
461 | | UTHFreePackets(&p, 1); |
462 | | PASS; |
463 | | } |
464 | | |
465 | | /** \test Send Modbus Force Listen Only Mode request. */ |
466 | | static int ModbusParserTest04(void) { |
467 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
468 | | Flow f; |
469 | | TcpSession ssn; |
470 | | |
471 | | FAIL_IF_NULL(alp_tctx); |
472 | | |
473 | | memset(&f, 0, sizeof(f)); |
474 | | memset(&ssn, 0, sizeof(ssn)); |
475 | | |
476 | | FLOW_INITIALIZE(&f); |
477 | | f.protoctx = (void *)&ssn; |
478 | | f.proto = IPPROTO_TCP; |
479 | | f.alproto = ALPROTO_MODBUS; |
480 | | |
481 | | StreamTcpInitConfig(true); |
482 | | |
483 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
484 | | STREAM_TOSERVER, forceListenOnlyMode, |
485 | | sizeof(forceListenOnlyMode)); |
486 | | FAIL_IF_NOT(r == 0); |
487 | | |
488 | | ModbusState *modbus_state = f.alstate; |
489 | | FAIL_IF_NULL(modbus_state); |
490 | | |
491 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
492 | | FAIL_IF_NULL(request._0); |
493 | | |
494 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 8); |
495 | | FAIL_IF_NOT(rs_modbus_message_get_subfunction(&request) == 4); |
496 | | |
497 | | AppLayerParserThreadCtxFree(alp_tctx); |
498 | | StreamTcpFreeConfig(true); |
499 | | FLOW_DESTROY(&f); |
500 | | PASS; |
501 | | } |
502 | | |
503 | | /** \test Send Modbus invalid Protocol version in request. */ |
504 | | static int ModbusParserTest05(void) { |
505 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
506 | | DetectEngineThreadCtx *det_ctx = NULL; |
507 | | Flow f; |
508 | | Packet *p = NULL; |
509 | | Signature *s = NULL; |
510 | | TcpSession ssn; |
511 | | ThreadVars tv; |
512 | | |
513 | | FAIL_IF_NULL(alp_tctx); |
514 | | |
515 | | memset(&tv, 0, sizeof(ThreadVars)); |
516 | | memset(&f, 0, sizeof(Flow)); |
517 | | memset(&ssn, 0, sizeof(TcpSession)); |
518 | | |
519 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
520 | | |
521 | | FLOW_INITIALIZE(&f); |
522 | | f.alproto = ALPROTO_MODBUS; |
523 | | f.protoctx = (void *)&ssn; |
524 | | f.proto = IPPROTO_TCP; |
525 | | f.alproto = ALPROTO_MODBUS; |
526 | | f.flags |= FLOW_IPV4; |
527 | | |
528 | | p->flow = &f; |
529 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
530 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
531 | | |
532 | | StreamTcpInitConfig(true); |
533 | | |
534 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
535 | | FAIL_IF_NULL(de_ctx); |
536 | | |
537 | | de_ctx->flags |= DE_QUIET; |
538 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
539 | | "(msg:\"Modbus invalid Protocol version\"; " |
540 | | "app-layer-event: " |
541 | | "modbus.invalid_protocol_id; " |
542 | | "sid:1;)"); |
543 | | FAIL_IF_NULL(s); |
544 | | |
545 | | SigGroupBuild(de_ctx); |
546 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
547 | | |
548 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
549 | | STREAM_TOSERVER, invalidProtocolIdReq, |
550 | | sizeof(invalidProtocolIdReq)); |
551 | | FAIL_IF_NOT(r == 0); |
552 | | |
553 | | ModbusState *modbus_state = f.alstate; |
554 | | FAIL_IF_NULL(modbus_state); |
555 | | |
556 | | /* do detect */ |
557 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
558 | | |
559 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
560 | | |
561 | | SigGroupCleanup(de_ctx); |
562 | | SigCleanSignatures(de_ctx); |
563 | | |
564 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
565 | | DetectEngineCtxFree(de_ctx); |
566 | | |
567 | | AppLayerParserThreadCtxFree(alp_tctx); |
568 | | StreamTcpFreeConfig(true); |
569 | | FLOW_DESTROY(&f); |
570 | | UTHFreePackets(&p, 1); |
571 | | PASS; |
572 | | } |
573 | | |
574 | | /** \test Send Modbus unsolicited response. */ |
575 | | static int ModbusParserTest06(void) { |
576 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
577 | | DetectEngineThreadCtx *det_ctx = NULL; |
578 | | Flow f; |
579 | | Packet *p = NULL; |
580 | | Signature *s = NULL; |
581 | | TcpSession ssn; |
582 | | ThreadVars tv; |
583 | | |
584 | | FAIL_IF_NULL(alp_tctx); |
585 | | |
586 | | memset(&tv, 0, sizeof(ThreadVars)); |
587 | | memset(&f, 0, sizeof(Flow)); |
588 | | memset(&ssn, 0, sizeof(TcpSession)); |
589 | | |
590 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
591 | | |
592 | | FLOW_INITIALIZE(&f); |
593 | | f.alproto = ALPROTO_MODBUS; |
594 | | f.protoctx = (void *)&ssn; |
595 | | f.proto = IPPROTO_TCP; |
596 | | f.alproto = ALPROTO_MODBUS; |
597 | | f.flags |= FLOW_IPV4; |
598 | | |
599 | | p->flow = &f; |
600 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
601 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
602 | | |
603 | | StreamTcpInitConfig(true); |
604 | | |
605 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
606 | | FAIL_IF_NULL(de_ctx); |
607 | | |
608 | | de_ctx->flags |= DE_QUIET; |
609 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
610 | | "(msg:\"Modbus unsolicited response\"; " |
611 | | "app-layer-event: " |
612 | | "modbus.unsolicited_response; " |
613 | | "sid:1;)"); |
614 | | FAIL_IF_NULL(s); |
615 | | |
616 | | SigGroupBuild(de_ctx); |
617 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
618 | | |
619 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
620 | | STREAM_TOCLIENT, readCoilsRsp, |
621 | | sizeof(readCoilsRsp)); |
622 | | FAIL_IF_NOT(r == 0); |
623 | | |
624 | | ModbusState *modbus_state = f.alstate; |
625 | | FAIL_IF_NULL(modbus_state); |
626 | | |
627 | | /* do detect */ |
628 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
629 | | |
630 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
631 | | |
632 | | SigGroupCleanup(de_ctx); |
633 | | SigCleanSignatures(de_ctx); |
634 | | |
635 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
636 | | DetectEngineCtxFree(de_ctx); |
637 | | |
638 | | AppLayerParserThreadCtxFree(alp_tctx); |
639 | | StreamTcpFreeConfig(true); |
640 | | FLOW_DESTROY(&f); |
641 | | UTHFreePackets(&p, 1); |
642 | | PASS; |
643 | | } |
644 | | |
645 | | /** \test Send Modbus invalid Length request. */ |
646 | | static int ModbusParserTest07(void) { |
647 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
648 | | DetectEngineThreadCtx *det_ctx = NULL; |
649 | | Flow f; |
650 | | Packet *p = NULL; |
651 | | Signature *s = NULL; |
652 | | TcpSession ssn; |
653 | | ThreadVars tv; |
654 | | |
655 | | FAIL_IF_NULL(alp_tctx); |
656 | | |
657 | | memset(&tv, 0, sizeof(ThreadVars)); |
658 | | memset(&f, 0, sizeof(Flow)); |
659 | | memset(&ssn, 0, sizeof(TcpSession)); |
660 | | |
661 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
662 | | |
663 | | FLOW_INITIALIZE(&f); |
664 | | f.alproto = ALPROTO_MODBUS; |
665 | | f.protoctx = (void *)&ssn; |
666 | | f.proto = IPPROTO_TCP; |
667 | | f.alproto = ALPROTO_MODBUS; |
668 | | f.flags |= FLOW_IPV4; |
669 | | |
670 | | p->flow = &f; |
671 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
672 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
673 | | |
674 | | StreamTcpInitConfig(true); |
675 | | |
676 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
677 | | FAIL_IF_NULL(de_ctx); |
678 | | |
679 | | de_ctx->flags |= DE_QUIET; |
680 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
681 | | "(msg:\"Modbus invalid Length\"; " |
682 | | "app-layer-event: " |
683 | | "modbus.invalid_length; " |
684 | | "sid:1;)"); |
685 | | FAIL_IF_NULL(s); |
686 | | |
687 | | SigGroupBuild(de_ctx); |
688 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
689 | | |
690 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
691 | | STREAM_TOSERVER, |
692 | | invalidLengthWriteMultipleRegistersReq, |
693 | | sizeof(invalidLengthWriteMultipleRegistersReq)); |
694 | | FAIL_IF_NOT(r == 1); |
695 | | |
696 | | ModbusState *modbus_state = f.alstate; |
697 | | FAIL_IF_NULL(modbus_state); |
698 | | |
699 | | /* do detect */ |
700 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
701 | | |
702 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
703 | | |
704 | | SigGroupCleanup(de_ctx); |
705 | | SigCleanSignatures(de_ctx); |
706 | | |
707 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
708 | | DetectEngineCtxFree(de_ctx); |
709 | | |
710 | | AppLayerParserThreadCtxFree(alp_tctx); |
711 | | StreamTcpFreeConfig(true); |
712 | | FLOW_DESTROY(&f); |
713 | | UTHFreePackets(&p, 1); |
714 | | PASS; |
715 | | } |
716 | | |
717 | | /** \test Send Modbus Read Coils request and error response with Exception code invalid. */ |
718 | | static int ModbusParserTest08(void) { |
719 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
720 | | DetectEngineThreadCtx *det_ctx = NULL; |
721 | | Flow f; |
722 | | Packet *p = NULL; |
723 | | Signature *s = NULL; |
724 | | TcpSession ssn; |
725 | | ThreadVars tv; |
726 | | |
727 | | FAIL_IF_NULL(alp_tctx); |
728 | | |
729 | | memset(&tv, 0, sizeof(ThreadVars)); |
730 | | memset(&f, 0, sizeof(Flow)); |
731 | | memset(&ssn, 0, sizeof(TcpSession)); |
732 | | |
733 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
734 | | |
735 | | FLOW_INITIALIZE(&f); |
736 | | f.alproto = ALPROTO_MODBUS; |
737 | | f.protoctx = (void *)&ssn; |
738 | | f.proto = IPPROTO_TCP; |
739 | | f.alproto = ALPROTO_MODBUS; |
740 | | f.flags |= FLOW_IPV4; |
741 | | |
742 | | p->flow = &f; |
743 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
744 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
745 | | |
746 | | StreamTcpInitConfig(true); |
747 | | |
748 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
749 | | FAIL_IF_NULL(de_ctx); |
750 | | |
751 | | de_ctx->flags |= DE_QUIET; |
752 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
753 | | "(msg:\"Modbus Exception code invalid\"; " |
754 | | "app-layer-event: " |
755 | | "modbus.invalid_exception_code; " |
756 | | "sid:1;)"); |
757 | | FAIL_IF_NULL(s); |
758 | | |
759 | | SigGroupBuild(de_ctx); |
760 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
761 | | |
762 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
763 | | STREAM_TOSERVER, readCoilsReq, |
764 | | sizeof(readCoilsReq)); |
765 | | FAIL_IF_NOT(r == 0); |
766 | | |
767 | | ModbusState *modbus_state = f.alstate; |
768 | | FAIL_IF_NULL(modbus_state); |
769 | | |
770 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
771 | | FAIL_IF_NULL(request._0); |
772 | | |
773 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1); |
774 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890); |
775 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19); |
776 | | |
777 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
778 | | STREAM_TOCLIENT, readCoilsErrorRsp, |
779 | | sizeof(readCoilsErrorRsp)); |
780 | | FAIL_IF_NOT(r == 0); |
781 | | |
782 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
783 | | |
784 | | /* do detect */ |
785 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
786 | | |
787 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
788 | | |
789 | | SigGroupCleanup(de_ctx); |
790 | | SigCleanSignatures(de_ctx); |
791 | | |
792 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
793 | | DetectEngineCtxFree(de_ctx); |
794 | | |
795 | | AppLayerParserThreadCtxFree(alp_tctx); |
796 | | StreamTcpFreeConfig(true); |
797 | | FLOW_DESTROY(&f); |
798 | | UTHFreePackets(&p, 1); |
799 | | PASS; |
800 | | } |
801 | | |
802 | | /** \test Modbus fragmentation - 1 ADU over 2 TCP packets. */ |
803 | | static int ModbusParserTest09(void) { |
804 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
805 | | Flow f; |
806 | | TcpSession ssn; |
807 | | |
808 | | uint32_t input_len = sizeof(readCoilsReq), part2_len = 3; |
809 | | uint8_t *input = readCoilsReq; |
810 | | |
811 | | FAIL_IF_NULL(alp_tctx); |
812 | | |
813 | | memset(&f, 0, sizeof(f)); |
814 | | memset(&ssn, 0, sizeof(ssn)); |
815 | | |
816 | | FLOW_INITIALIZE(&f); |
817 | | f.protoctx = (void *)&ssn; |
818 | | f.proto = IPPROTO_TCP; |
819 | | f.alproto = ALPROTO_MODBUS; |
820 | | |
821 | | StreamTcpInitConfig(true); |
822 | | |
823 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
824 | | STREAM_TOSERVER, input, input_len - part2_len); |
825 | | FAIL_IF_NOT(r == 1); |
826 | | |
827 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
828 | | STREAM_TOSERVER, input, input_len); |
829 | | FAIL_IF_NOT(r == 0); |
830 | | |
831 | | ModbusState *modbus_state = f.alstate; |
832 | | FAIL_IF_NULL(modbus_state); |
833 | | |
834 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
835 | | FAIL_IF_NULL(request._0); |
836 | | |
837 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 1); |
838 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_address(&request) == 0x7890); |
839 | | FAIL_IF_NOT(rs_modbus_message_get_read_request_quantity(&request) == 19); |
840 | | |
841 | | input_len = sizeof(readCoilsRsp); |
842 | | part2_len = 10; |
843 | | input = readCoilsRsp; |
844 | | |
845 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
846 | | STREAM_TOCLIENT, input, input_len - part2_len); |
847 | | FAIL_IF_NOT(r == 1); |
848 | | |
849 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
850 | | STREAM_TOCLIENT, input, input_len); |
851 | | FAIL_IF_NOT(r == 0); |
852 | | |
853 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
854 | | |
855 | | AppLayerParserThreadCtxFree(alp_tctx); |
856 | | StreamTcpFreeConfig(true); |
857 | | FLOW_DESTROY(&f); |
858 | | PASS; |
859 | | } |
860 | | |
861 | | /** \test Modbus fragmentation - 2 ADU in 1 TCP packet. */ |
862 | | static int ModbusParserTest10(void) { |
863 | | uint32_t input_len = sizeof(readCoilsReq) + sizeof(writeMultipleRegistersReq); |
864 | | uint8_t *input, *ptr; |
865 | | |
866 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
867 | | Flow f; |
868 | | TcpSession ssn; |
869 | | |
870 | | FAIL_IF_NULL(alp_tctx); |
871 | | |
872 | | input = (uint8_t *) SCMalloc (input_len * sizeof(uint8_t)); |
873 | | FAIL_IF_NULL(input); |
874 | | |
875 | | memcpy(input, readCoilsReq, sizeof(readCoilsReq)); |
876 | | memcpy(input + sizeof(readCoilsReq), writeMultipleRegistersReq, sizeof(writeMultipleRegistersReq)); |
877 | | |
878 | | memset(&f, 0, sizeof(f)); |
879 | | memset(&ssn, 0, sizeof(ssn)); |
880 | | |
881 | | FLOW_INITIALIZE(&f); |
882 | | f.protoctx = (void *)&ssn; |
883 | | f.proto = IPPROTO_TCP; |
884 | | f.alproto = ALPROTO_MODBUS; |
885 | | |
886 | | StreamTcpInitConfig(true); |
887 | | |
888 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
889 | | STREAM_TOSERVER, input, input_len); |
890 | | FAIL_IF_NOT(r == 0); |
891 | | |
892 | | ModbusState *modbus_state = f.alstate; |
893 | | FAIL_IF_NULL(modbus_state); |
894 | | |
895 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 2); |
896 | | |
897 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 1); |
898 | | FAIL_IF_NULL(request._0); |
899 | | |
900 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 16); |
901 | | FAIL_IF_NOT(rs_modbus_message_get_write_multreq_address(&request) == 0x01); |
902 | | FAIL_IF_NOT(rs_modbus_message_get_write_multreq_quantity(&request) == 2); |
903 | | |
904 | | size_t data_len; |
905 | | uint8_t const *data = rs_modbus_message_get_write_multreq_data(&request, &data_len); |
906 | | FAIL_IF_NOT(data_len == 4); |
907 | | FAIL_IF_NOT(data[0] == 0x00); |
908 | | FAIL_IF_NOT(data[1] == 0x0A); |
909 | | FAIL_IF_NOT(data[2] == 0x01); |
910 | | FAIL_IF_NOT(data[3] == 0x02); |
911 | | |
912 | | input_len = sizeof(readCoilsRsp) + sizeof(writeMultipleRegistersRsp); |
913 | | |
914 | | ptr = (uint8_t *) SCRealloc (input, input_len * sizeof(uint8_t)); |
915 | | FAIL_IF_NULL(ptr); |
916 | | input = ptr; |
917 | | |
918 | | memcpy(input, readCoilsRsp, sizeof(readCoilsRsp)); |
919 | | memcpy(input + sizeof(readCoilsRsp), writeMultipleRegistersRsp, sizeof(writeMultipleRegistersRsp)); |
920 | | |
921 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, input, input_len); |
922 | | FAIL_IF_NOT(r == 0); |
923 | | |
924 | | SCFree(input); |
925 | | AppLayerParserThreadCtxFree(alp_tctx); |
926 | | StreamTcpFreeConfig(true); |
927 | | FLOW_DESTROY(&f); |
928 | | PASS; |
929 | | } |
930 | | |
931 | | /** \test Send Modbus exceed Length request. */ |
932 | | static int ModbusParserTest11(void) { |
933 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
934 | | DetectEngineThreadCtx *det_ctx = NULL; |
935 | | Flow f; |
936 | | Packet *p = NULL; |
937 | | Signature *s = NULL; |
938 | | TcpSession ssn; |
939 | | ThreadVars tv; |
940 | | |
941 | | size_t input_len = 65536; |
942 | | uint8_t *input = SCCalloc(1, input_len); |
943 | | |
944 | | FAIL_IF(input == NULL); |
945 | | |
946 | | memcpy(input, exceededLengthWriteMultipleRegistersReq, |
947 | | sizeof(exceededLengthWriteMultipleRegistersReq)); |
948 | | |
949 | | FAIL_IF(alp_tctx == NULL); |
950 | | |
951 | | memset(&tv, 0, sizeof(ThreadVars)); |
952 | | memset(&f, 0, sizeof(Flow)); |
953 | | memset(&ssn, 0, sizeof(TcpSession)); |
954 | | |
955 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
956 | | |
957 | | FLOW_INITIALIZE(&f); |
958 | | f.alproto = ALPROTO_MODBUS; |
959 | | f.protoctx = (void *)&ssn; |
960 | | f.proto = IPPROTO_TCP; |
961 | | f.alproto = ALPROTO_MODBUS; |
962 | | f.flags |= FLOW_IPV4; |
963 | | |
964 | | p->flow = &f; |
965 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
966 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
967 | | |
968 | | StreamTcpInitConfig(true); |
969 | | |
970 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
971 | | FAIL_IF_NULL(de_ctx); |
972 | | |
973 | | de_ctx->flags |= DE_QUIET; |
974 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
975 | | "(msg:\"Modbus invalid Length\"; " |
976 | | "app-layer-event: " |
977 | | "modbus.invalid_length; " |
978 | | "sid:1;)"); |
979 | | FAIL_IF_NULL(s); |
980 | | |
981 | | SigGroupBuild(de_ctx); |
982 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
983 | | |
984 | | int r = AppLayerParserParse( |
985 | | NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, input, input_len); |
986 | | FAIL_IF_NOT(r == 0); |
987 | | |
988 | | ModbusState *modbus_state = f.alstate; |
989 | | FAIL_IF_NULL(modbus_state); |
990 | | |
991 | | /* do detect */ |
992 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
993 | | |
994 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
995 | | |
996 | | SigGroupCleanup(de_ctx); |
997 | | SigCleanSignatures(de_ctx); |
998 | | |
999 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
1000 | | DetectEngineCtxFree(de_ctx); |
1001 | | |
1002 | | AppLayerParserThreadCtxFree(alp_tctx); |
1003 | | StreamTcpFreeConfig(true); |
1004 | | FLOW_DESTROY(&f); |
1005 | | UTHFreePackets(&p, 1); |
1006 | | PASS; |
1007 | | } |
1008 | | |
1009 | | /** \test Send Modbus invalid PDU Length. */ |
1010 | | static int ModbusParserTest12(void) { |
1011 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1012 | | DetectEngineThreadCtx *det_ctx = NULL; |
1013 | | Flow f; |
1014 | | Packet *p = NULL; |
1015 | | Signature *s = NULL; |
1016 | | TcpSession ssn; |
1017 | | ThreadVars tv; |
1018 | | |
1019 | | FAIL_IF_NULL(alp_tctx); |
1020 | | |
1021 | | memset(&tv, 0, sizeof(ThreadVars)); |
1022 | | memset(&f, 0, sizeof(Flow)); |
1023 | | memset(&ssn, 0, sizeof(TcpSession)); |
1024 | | |
1025 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1026 | | |
1027 | | FLOW_INITIALIZE(&f); |
1028 | | f.alproto = ALPROTO_MODBUS; |
1029 | | f.protoctx = (void *)&ssn; |
1030 | | f.proto = IPPROTO_TCP; |
1031 | | f.alproto = ALPROTO_MODBUS; |
1032 | | f.flags |= FLOW_IPV4; |
1033 | | |
1034 | | p->flow = &f; |
1035 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
1036 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
1037 | | |
1038 | | StreamTcpInitConfig(true); |
1039 | | |
1040 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1041 | | FAIL_IF_NULL(de_ctx); |
1042 | | |
1043 | | de_ctx->flags |= DE_QUIET; |
1044 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
1045 | | "(msg:\"Modbus invalid Length\"; " |
1046 | | "app-layer-event: " |
1047 | | "modbus.invalid_length; " |
1048 | | "sid:1;)"); |
1049 | | FAIL_IF_NULL(s); |
1050 | | |
1051 | | SigGroupBuild(de_ctx); |
1052 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
1053 | | |
1054 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1055 | | STREAM_TOSERVER, |
1056 | | invalidLengthPDUWriteMultipleRegistersReq, |
1057 | | sizeof(invalidLengthPDUWriteMultipleRegistersReq)); |
1058 | | FAIL_IF_NOT(r == 0); |
1059 | | |
1060 | | ModbusState *modbus_state = f.alstate; |
1061 | | FAIL_IF_NULL(modbus_state); |
1062 | | |
1063 | | /* do detect */ |
1064 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
1065 | | |
1066 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
1067 | | |
1068 | | SigGroupCleanup(de_ctx); |
1069 | | SigCleanSignatures(de_ctx); |
1070 | | |
1071 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
1072 | | DetectEngineCtxFree(de_ctx); |
1073 | | |
1074 | | AppLayerParserThreadCtxFree(alp_tctx); |
1075 | | StreamTcpFreeConfig(true); |
1076 | | FLOW_DESTROY(&f); |
1077 | | UTHFreePackets(&p, 1); |
1078 | | PASS; |
1079 | | } |
1080 | | |
1081 | | /** \test Send Modbus Mask Write register request/response. */ |
1082 | | static int ModbusParserTest13(void) { |
1083 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1084 | | Flow f; |
1085 | | TcpSession ssn; |
1086 | | |
1087 | | FAIL_IF_NULL(alp_tctx); |
1088 | | |
1089 | | memset(&f, 0, sizeof(f)); |
1090 | | memset(&ssn, 0, sizeof(ssn)); |
1091 | | |
1092 | | FLOW_INITIALIZE(&f); |
1093 | | f.protoctx = (void *)&ssn; |
1094 | | f.proto = IPPROTO_TCP; |
1095 | | f.alproto = ALPROTO_MODBUS; |
1096 | | |
1097 | | StreamTcpInitConfig(true); |
1098 | | |
1099 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1100 | | STREAM_TOSERVER, maskWriteRegisterReq, |
1101 | | sizeof(maskWriteRegisterReq)); |
1102 | | FAIL_IF_NOT(r == 0); |
1103 | | |
1104 | | ModbusState *modbus_state = f.alstate; |
1105 | | FAIL_IF_NULL(modbus_state); |
1106 | | |
1107 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
1108 | | FAIL_IF_NULL(request._0); |
1109 | | |
1110 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 22); |
1111 | | FAIL_IF_NOT(rs_modbus_message_get_and_mask(&request) == 0x00F2); |
1112 | | FAIL_IF_NOT(rs_modbus_message_get_or_mask(&request) == 0x0025); |
1113 | | |
1114 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1115 | | STREAM_TOCLIENT, maskWriteRegisterRsp, |
1116 | | sizeof(maskWriteRegisterRsp)); |
1117 | | FAIL_IF_NOT(r == 0); |
1118 | | |
1119 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
1120 | | |
1121 | | AppLayerParserThreadCtxFree(alp_tctx); |
1122 | | StreamTcpFreeConfig(true); |
1123 | | FLOW_DESTROY(&f); |
1124 | | PASS; |
1125 | | } |
1126 | | |
1127 | | /** \test Send Modbus Write single register request/response. */ |
1128 | | static int ModbusParserTest14(void) { |
1129 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1130 | | Flow f; |
1131 | | TcpSession ssn; |
1132 | | |
1133 | | FAIL_IF_NULL(alp_tctx); |
1134 | | |
1135 | | memset(&f, 0, sizeof(f)); |
1136 | | memset(&ssn, 0, sizeof(ssn)); |
1137 | | |
1138 | | FLOW_INITIALIZE(&f); |
1139 | | f.protoctx = (void *)&ssn; |
1140 | | f.proto = IPPROTO_TCP; |
1141 | | f.alproto = ALPROTO_MODBUS; |
1142 | | |
1143 | | StreamTcpInitConfig(true); |
1144 | | |
1145 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1146 | | STREAM_TOSERVER, writeSingleRegisterReq, |
1147 | | sizeof(writeSingleRegisterReq)); |
1148 | | FAIL_IF_NOT(r == 0); |
1149 | | |
1150 | | ModbusState *modbus_state = f.alstate; |
1151 | | FAIL_IF_NULL(modbus_state); |
1152 | | |
1153 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
1154 | | FAIL_IF_NULL(request._0); |
1155 | | |
1156 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 6); |
1157 | | FAIL_IF_NOT(rs_modbus_message_get_write_address(&request) == 0x0001); |
1158 | | FAIL_IF_NOT(rs_modbus_message_get_write_data(&request) == 0x0003); |
1159 | | |
1160 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1161 | | STREAM_TOCLIENT, writeSingleRegisterRsp, |
1162 | | sizeof(writeSingleRegisterRsp)); |
1163 | | FAIL_IF_NOT(r == 0); |
1164 | | |
1165 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
1166 | | |
1167 | | AppLayerParserThreadCtxFree(alp_tctx); |
1168 | | StreamTcpFreeConfig(true); |
1169 | | FLOW_DESTROY(&f); |
1170 | | PASS; |
1171 | | } |
1172 | | |
1173 | | /** \test Send invalid Modbus Mask Write register request. */ |
1174 | | static int ModbusParserTest15(void) { |
1175 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1176 | | DetectEngineThreadCtx *det_ctx = NULL; |
1177 | | Flow f; |
1178 | | Packet *p = NULL; |
1179 | | Signature *s = NULL; |
1180 | | TcpSession ssn; |
1181 | | ThreadVars tv; |
1182 | | |
1183 | | FAIL_IF_NULL(alp_tctx); |
1184 | | |
1185 | | memset(&tv, 0, sizeof(ThreadVars)); |
1186 | | memset(&f, 0, sizeof(f)); |
1187 | | memset(&ssn, 0, sizeof(ssn)); |
1188 | | |
1189 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1190 | | |
1191 | | FLOW_INITIALIZE(&f); |
1192 | | f.alproto = ALPROTO_MODBUS; |
1193 | | f.protoctx = (void *)&ssn; |
1194 | | f.proto = IPPROTO_TCP; |
1195 | | f.alproto = ALPROTO_MODBUS; |
1196 | | f.flags |= FLOW_IPV4; |
1197 | | |
1198 | | p->flow = &f; |
1199 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
1200 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
1201 | | |
1202 | | StreamTcpInitConfig(true); |
1203 | | |
1204 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1205 | | FAIL_IF_NULL(de_ctx); |
1206 | | |
1207 | | de_ctx->flags |= DE_QUIET; |
1208 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
1209 | | "(msg:\"Modbus invalid Length\"; " |
1210 | | "app-layer-event: " |
1211 | | "modbus.invalid_length; " |
1212 | | "sid:1;)"); |
1213 | | FAIL_IF_NULL(s); |
1214 | | |
1215 | | SigGroupBuild(de_ctx); |
1216 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
1217 | | |
1218 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1219 | | STREAM_TOSERVER, invalidMaskWriteRegisterReq, |
1220 | | sizeof(invalidMaskWriteRegisterReq)); |
1221 | | FAIL_IF_NOT(r == 0); |
1222 | | |
1223 | | ModbusState *modbus_state = f.alstate; |
1224 | | FAIL_IF_NULL(modbus_state); |
1225 | | |
1226 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
1227 | | FAIL_IF_NULL(request._0); |
1228 | | |
1229 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 22); |
1230 | | |
1231 | | /* do detect */ |
1232 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
1233 | | |
1234 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
1235 | | |
1236 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1237 | | STREAM_TOCLIENT, maskWriteRegisterRsp, |
1238 | | sizeof(maskWriteRegisterRsp)); |
1239 | | FAIL_IF_NOT(r == 0); |
1240 | | |
1241 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
1242 | | ModbusMessage response = rs_modbus_state_get_tx_response(modbus_state, 0); |
1243 | | FAIL_IF_NULL(response._0); |
1244 | | |
1245 | | FAIL_IF_NOT(rs_modbus_message_get_function(&response) == 22); |
1246 | | |
1247 | | SigGroupCleanup(de_ctx); |
1248 | | SigCleanSignatures(de_ctx); |
1249 | | |
1250 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
1251 | | DetectEngineCtxFree(de_ctx); |
1252 | | |
1253 | | AppLayerParserThreadCtxFree(alp_tctx); |
1254 | | StreamTcpFreeConfig(true); |
1255 | | FLOW_DESTROY(&f); |
1256 | | UTHFreePackets(&p, 1); |
1257 | | PASS; |
1258 | | } |
1259 | | |
1260 | | /** \test Send invalid Modbus Mask Write register request. */ |
1261 | | static int ModbusParserTest16(void) { |
1262 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1263 | | DetectEngineThreadCtx *det_ctx = NULL; |
1264 | | Flow f; |
1265 | | Packet *p = NULL; |
1266 | | Signature *s = NULL; |
1267 | | TcpSession ssn; |
1268 | | ThreadVars tv; |
1269 | | |
1270 | | FAIL_IF_NULL(alp_tctx); |
1271 | | |
1272 | | memset(&tv, 0, sizeof(ThreadVars)); |
1273 | | memset(&f, 0, sizeof(f)); |
1274 | | memset(&ssn, 0, sizeof(ssn)); |
1275 | | |
1276 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1277 | | |
1278 | | FLOW_INITIALIZE(&f); |
1279 | | f.alproto = ALPROTO_MODBUS; |
1280 | | f.protoctx = (void *)&ssn; |
1281 | | f.proto = IPPROTO_TCP; |
1282 | | f.alproto = ALPROTO_MODBUS; |
1283 | | f.flags |= FLOW_IPV4; |
1284 | | |
1285 | | p->flow = &f; |
1286 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
1287 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
1288 | | |
1289 | | StreamTcpInitConfig(true); |
1290 | | |
1291 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1292 | | FAIL_IF_NULL(de_ctx); |
1293 | | |
1294 | | de_ctx->flags |= DE_QUIET; |
1295 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
1296 | | "(msg:\"Modbus invalid Length\"; " |
1297 | | "app-layer-event: " |
1298 | | "modbus.invalid_length; " |
1299 | | "sid:1;)"); |
1300 | | FAIL_IF_NULL(s); |
1301 | | |
1302 | | SigGroupBuild(de_ctx); |
1303 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
1304 | | |
1305 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1306 | | STREAM_TOSERVER, |
1307 | | invalidWriteSingleRegisterReq, |
1308 | | sizeof(invalidWriteSingleRegisterReq)); |
1309 | | FAIL_IF_NOT(r == 0); |
1310 | | |
1311 | | ModbusState *modbus_state = f.alstate; |
1312 | | FAIL_IF_NULL(modbus_state); |
1313 | | |
1314 | | ModbusMessage request = rs_modbus_state_get_tx_request(modbus_state, 0); |
1315 | | FAIL_IF_NULL(request._0); |
1316 | | |
1317 | | FAIL_IF_NOT(rs_modbus_message_get_function(&request) == 6); |
1318 | | size_t data_len; |
1319 | | const uint8_t *data = rs_modbus_message_get_bytevec_data(&request, &data_len); |
1320 | | FAIL_IF_NOT(data_len == 2); |
1321 | | FAIL_IF_NOT(data[0] == 0x00); |
1322 | | FAIL_IF_NOT(data[1] == 0x01); |
1323 | | |
1324 | | /* do detect */ |
1325 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
1326 | | |
1327 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
1328 | | |
1329 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1330 | | STREAM_TOCLIENT, writeSingleRegisterRsp, |
1331 | | sizeof(writeSingleRegisterRsp)); |
1332 | | FAIL_IF_NOT(r == 0); |
1333 | | |
1334 | | FAIL_IF_NOT(rs_modbus_state_get_tx_count(modbus_state) == 1); |
1335 | | ModbusMessage response = rs_modbus_state_get_tx_response(modbus_state, 0); |
1336 | | FAIL_IF_NULL(response._0); |
1337 | | |
1338 | | FAIL_IF_NOT(rs_modbus_message_get_function(&response) == 6); |
1339 | | FAIL_IF_NOT(rs_modbus_message_get_write_address(&response) == 0x0001); |
1340 | | |
1341 | | SigGroupCleanup(de_ctx); |
1342 | | SigCleanSignatures(de_ctx); |
1343 | | |
1344 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
1345 | | DetectEngineCtxFree(de_ctx); |
1346 | | |
1347 | | AppLayerParserThreadCtxFree(alp_tctx); |
1348 | | StreamTcpFreeConfig(true); |
1349 | | FLOW_DESTROY(&f); |
1350 | | UTHFreePackets(&p, 1); |
1351 | | PASS;} |
1352 | | |
1353 | | /** \test Checks if stream_depth is correct */ |
1354 | | static int ModbusParserTest17(void) { |
1355 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1356 | | Flow f; |
1357 | | TcpSession ssn; |
1358 | | |
1359 | | FAIL_IF_NULL(alp_tctx); |
1360 | | |
1361 | | memset(&f, 0, sizeof(f)); |
1362 | | memset(&ssn, 0, sizeof(ssn)); |
1363 | | |
1364 | | FLOW_INITIALIZE(&f); |
1365 | | f.protoctx = (void *)&ssn; |
1366 | | f.proto = IPPROTO_TCP; |
1367 | | f.alproto = ALPROTO_MODBUS; |
1368 | | |
1369 | | StreamTcpInitConfig(true); |
1370 | | |
1371 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, |
1372 | | readCoilsReq, sizeof(readCoilsReq)); |
1373 | | FAIL_IF(r != 0); |
1374 | | |
1375 | | FAIL_IF(f.alstate == NULL); |
1376 | | |
1377 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1378 | | |
1379 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, |
1380 | | readCoilsRsp, sizeof(readCoilsRsp)); |
1381 | | FAIL_IF(r != 0); |
1382 | | |
1383 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1384 | | |
1385 | | AppLayerParserThreadCtxFree(alp_tctx); |
1386 | | StreamTcpFreeConfig(true); |
1387 | | FLOW_DESTROY(&f); |
1388 | | PASS; |
1389 | | } |
1390 | | |
1391 | | /*/ \test Checks if stream depth is correct over 2 TCP packets */ |
1392 | | static int ModbusParserTest18(void) { |
1393 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1394 | | Flow f; |
1395 | | TcpSession ssn; |
1396 | | |
1397 | | uint32_t input_len = sizeof(readCoilsReq), part2_len = 3; |
1398 | | uint8_t *input = readCoilsReq; |
1399 | | |
1400 | | FAIL_IF_NULL(alp_tctx); |
1401 | | |
1402 | | memset(&f, 0, sizeof(f)); |
1403 | | memset(&ssn, 0, sizeof(ssn)); |
1404 | | |
1405 | | FLOW_INITIALIZE(&f); |
1406 | | f.protoctx = (void *)&ssn; |
1407 | | f.proto = IPPROTO_TCP; |
1408 | | f.alproto = ALPROTO_MODBUS; |
1409 | | |
1410 | | StreamTcpInitConfig(true); |
1411 | | |
1412 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, |
1413 | | input, input_len - part2_len); |
1414 | | FAIL_IF(r != 1); |
1415 | | |
1416 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1417 | | |
1418 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOSERVER, |
1419 | | input, input_len); |
1420 | | FAIL_IF(r != 0); |
1421 | | |
1422 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1423 | | |
1424 | | FAIL_IF(f.alstate == NULL); |
1425 | | |
1426 | | input_len = sizeof(readCoilsRsp); |
1427 | | part2_len = 10; |
1428 | | input = readCoilsRsp; |
1429 | | |
1430 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, |
1431 | | input, input_len - part2_len); |
1432 | | FAIL_IF(r != 1); |
1433 | | |
1434 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1435 | | |
1436 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, STREAM_TOCLIENT, |
1437 | | input, input_len); |
1438 | | FAIL_IF(r != 0); |
1439 | | |
1440 | | FAIL_IF(((TcpSession *)(f.protoctx))->reassembly_depth != MODBUS_CONFIG_DEFAULT_STREAM_DEPTH); |
1441 | | |
1442 | | AppLayerParserThreadCtxFree(alp_tctx); |
1443 | | StreamTcpFreeConfig(true); |
1444 | | FLOW_DESTROY(&f); |
1445 | | PASS; |
1446 | | } |
1447 | | |
1448 | | /** \test Send Modbus invalid function. */ |
1449 | | static int ModbusParserTest19(void) { |
1450 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1451 | | DetectEngineThreadCtx *det_ctx = NULL; |
1452 | | Flow f; |
1453 | | Packet *p = NULL; |
1454 | | Signature *s = NULL; |
1455 | | TcpSession ssn; |
1456 | | ThreadVars tv; |
1457 | | |
1458 | | FAIL_IF_NULL(alp_tctx); |
1459 | | |
1460 | | memset(&tv, 0, sizeof(ThreadVars)); |
1461 | | memset(&f, 0, sizeof(Flow)); |
1462 | | memset(&ssn, 0, sizeof(TcpSession)); |
1463 | | |
1464 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1465 | | |
1466 | | FLOW_INITIALIZE(&f); |
1467 | | f.alproto = ALPROTO_MODBUS; |
1468 | | f.protoctx = (void *)&ssn; |
1469 | | f.proto = IPPROTO_TCP; |
1470 | | f.alproto = ALPROTO_MODBUS; |
1471 | | f.flags |= FLOW_IPV4; |
1472 | | |
1473 | | p->flow = &f; |
1474 | | p->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; |
1475 | | p->flowflags |= FLOW_PKT_TOSERVER | FLOW_PKT_ESTABLISHED; |
1476 | | |
1477 | | StreamTcpInitConfig(true); |
1478 | | |
1479 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1480 | | FAIL_IF_NULL(de_ctx); |
1481 | | |
1482 | | de_ctx->flags |= DE_QUIET; |
1483 | | s = DetectEngineAppendSig(de_ctx, "alert modbus any any -> any any " |
1484 | | "(msg:\"Modbus invalid Function code\"; " |
1485 | | "app-layer-event: " |
1486 | | "modbus.invalid_function_code; " |
1487 | | "sid:1;)"); |
1488 | | FAIL_IF_NULL(s); |
1489 | | |
1490 | | SigGroupBuild(de_ctx); |
1491 | | DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); |
1492 | | |
1493 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_MODBUS, |
1494 | | STREAM_TOSERVER, |
1495 | | invalidFunctionCode, |
1496 | | sizeof(invalidFunctionCode)); |
1497 | | FAIL_IF_NOT(r == 0); |
1498 | | |
1499 | | ModbusState *modbus_state = f.alstate; |
1500 | | FAIL_IF_NULL(modbus_state); |
1501 | | |
1502 | | /* do detect */ |
1503 | | SigMatchSignatures(&tv, de_ctx, det_ctx, p); |
1504 | | |
1505 | | FAIL_IF_NOT(PacketAlertCheck(p, 1)); |
1506 | | |
1507 | | SigGroupCleanup(de_ctx); |
1508 | | SigCleanSignatures(de_ctx); |
1509 | | |
1510 | | DetectEngineThreadCtxDeinit(&tv, (void *)det_ctx); |
1511 | | DetectEngineCtxFree(de_ctx); |
1512 | | |
1513 | | AppLayerParserThreadCtxFree(alp_tctx); |
1514 | | StreamTcpFreeConfig(true); |
1515 | | FLOW_DESTROY(&f); |
1516 | | UTHFreePackets(&p, 1); |
1517 | | PASS; |
1518 | | } |
1519 | | #endif /* UNITTESTS */ |
1520 | | |
1521 | 0 | void ModbusParserRegisterTests(void) { |
1522 | | #ifdef UNITTESTS |
1523 | | UtRegisterTest("ModbusParserTest01 - Modbus Read Coils request", |
1524 | | ModbusParserTest01); |
1525 | | UtRegisterTest("ModbusParserTest02 - Modbus Write Multiple registers request", |
1526 | | ModbusParserTest02); |
1527 | | UtRegisterTest("ModbusParserTest03 - Modbus Read/Write Multiple registers request", |
1528 | | ModbusParserTest03); |
1529 | | UtRegisterTest("ModbusParserTest04 - Modbus Force Listen Only Mode request", |
1530 | | ModbusParserTest04); |
1531 | | UtRegisterTest("ModbusParserTest05 - Modbus invalid Protocol version", |
1532 | | ModbusParserTest05); |
1533 | | UtRegisterTest("ModbusParserTest06 - Modbus unsolicited response", |
1534 | | ModbusParserTest06); |
1535 | | UtRegisterTest("ModbusParserTest07 - Modbus invalid Length request", |
1536 | | ModbusParserTest07); |
1537 | | UtRegisterTest("ModbusParserTest08 - Modbus Exception code invalid", |
1538 | | ModbusParserTest08); |
1539 | | UtRegisterTest("ModbusParserTest09 - Modbus fragmentation - 1 ADU in 2 TCP packets", |
1540 | | ModbusParserTest09); |
1541 | | UtRegisterTest("ModbusParserTest10 - Modbus fragmentation - 2 ADU in 1 TCP packet", |
1542 | | ModbusParserTest10); |
1543 | | UtRegisterTest("ModbusParserTest11 - Modbus exceeded Length request", |
1544 | | ModbusParserTest11); |
1545 | | UtRegisterTest("ModbusParserTest12 - Modbus invalid PDU Length", |
1546 | | ModbusParserTest12); |
1547 | | UtRegisterTest("ModbusParserTest13 - Modbus Mask Write register request", |
1548 | | ModbusParserTest13); |
1549 | | UtRegisterTest("ModbusParserTest14 - Modbus Write single register request", |
1550 | | ModbusParserTest14); |
1551 | | UtRegisterTest("ModbusParserTest15 - Modbus invalid Mask Write register request", |
1552 | | ModbusParserTest15); |
1553 | | UtRegisterTest("ModbusParserTest16 - Modbus invalid Write single register request", |
1554 | | ModbusParserTest16); |
1555 | | UtRegisterTest("ModbusParserTest17 - Modbus stream depth", |
1556 | | ModbusParserTest17); |
1557 | | UtRegisterTest("ModbusParserTest18 - Modbus stream depth in 2 TCP packets", |
1558 | | ModbusParserTest18); |
1559 | | UtRegisterTest("ModbusParserTest19 - Modbus invalid Function code", |
1560 | | ModbusParserTest19); |
1561 | | #endif /* UNITTESTS */ |
1562 | 0 | } |