/src/suricata7/src/app-layer-dnp3.c
Line | Count | Source |
1 | | /* Copyright (C) 2015 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 | | * DNP3 protocol implementation |
22 | | */ |
23 | | |
24 | | #include "suricata-common.h" |
25 | | #include "suricata.h" |
26 | | #include "stream.h" |
27 | | #include "util-byte.h" |
28 | | #include "util-unittest.h" |
29 | | #include "util-hashlist.h" |
30 | | |
31 | | #include "util-print.h" |
32 | | #include "util-spm-bs.h" |
33 | | #include "util-enum.h" |
34 | | |
35 | | #include "app-layer.h" |
36 | | #include "app-layer-protos.h" |
37 | | #include "app-layer-parser.h" |
38 | | #include "app-layer-detect-proto.h" |
39 | | |
40 | | #include "app-layer-dnp3.h" |
41 | | #include "app-layer-dnp3-objects.h" |
42 | | |
43 | 0 | #define DNP3_DEFAULT_PORT "20000" |
44 | | |
45 | | /* Expected values for the start bytes. */ |
46 | 9.06M | #define DNP3_START_BYTE0 0x05 |
47 | 4.50M | #define DNP3_START_BYTE1 0x64 |
48 | | |
49 | | /* Minimum length for a DNP3 frame. */ |
50 | 51.6k | #define DNP3_MIN_LEN 5 |
51 | | |
52 | | /* Length of each CRC. */ |
53 | 52.1M | #define DNP3_CRC_LEN 2 |
54 | | |
55 | | /* DNP3 block size. After the link header a CRC is inserted after |
56 | | * after 16 bytes of data. */ |
57 | 36.0M | #define DNP3_BLOCK_SIZE 16 |
58 | | |
59 | | /* Maximum transport layer sequence number. */ |
60 | 367k | #define DNP3_MAX_TRAN_SEQNO 64 |
61 | | |
62 | | /* Maximum application layer sequence number. */ |
63 | | #define DNP3_MAX_APP_SEQNO 16 |
64 | | |
65 | | /* The number of bytes in the header that are counted as part of the |
66 | | * header length field. */ |
67 | 12.8M | #define DNP3_LINK_HDR_LEN 5 |
68 | | |
69 | | /* Link function codes. */ |
70 | | enum { |
71 | | DNP3_LINK_FC_CONFIRMED_USER_DATA = 3, |
72 | | DNP3_LINK_FC_UNCONFIRMED_USER_DATA |
73 | | }; |
74 | | |
75 | | /* Reserved addresses. */ |
76 | | #define DNP3_RESERVED_ADDR_MIN 0xfff0 |
77 | | #define DNP3_RESERVED_ADDR_MAX 0xfffb |
78 | | |
79 | | /* Source addresses must be < 0xfff0. */ |
80 | | #define DNP3_SRC_ADDR_MAX 0xfff0 |
81 | | |
82 | | #define DNP3_OBJ_TIME_SIZE 6 /* AKA UINT48. */ |
83 | | #define DNP3_OBJ_G12_V1_SIZE 11 |
84 | | #define DNP3_OBJ_G12_V2_SIZE 11 |
85 | | #define DNP3_OBJ_G12_V3_SIZE 1 |
86 | | |
87 | | /* Extract the prefix code from the object qualifier. */ |
88 | 1.37M | #define DNP3_OBJ_PREFIX(x) ((x >> 4) & 0x7) |
89 | | |
90 | | /* Extract the range code from the object qualifier. */ |
91 | 1.37M | #define DNP3_OBJ_RANGE(x) (x & 0xf) |
92 | | |
93 | | /* Default number of unreplied requests to be considered a flood. |
94 | | * |
95 | | * DNP3 is a request/response SCADA protocol with typically only 1-2 |
96 | | * transactions in flight. But set a limit high enough to allow for |
97 | | * some pipelining but reduce the chance of memory exhaustion |
98 | | * attacks. */ |
99 | | static uint64_t dnp3_max_tx = 32; |
100 | | |
101 | | /* The maximum number of points allowed per message (configurable). */ |
102 | | static uint64_t max_points = 16384; |
103 | | |
104 | | /* The maximum number of objects allowed per message (configurable). */ |
105 | | static uint64_t dnp3_max_objects = 2048; |
106 | | |
107 | | /* Decoder event map. */ |
108 | | SCEnumCharMap dnp3_decoder_event_table[] = { |
109 | | { "FLOODED", DNP3_DECODER_EVENT_FLOODED }, |
110 | | { "LEN_TOO_SMALL", DNP3_DECODER_EVENT_LEN_TOO_SMALL }, |
111 | | { "BAD_LINK_CRC", DNP3_DECODER_EVENT_BAD_LINK_CRC }, |
112 | | { "BAD_TRANSPORT_CRC", DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC }, |
113 | | { "MALFORMED", DNP3_DECODER_EVENT_MALFORMED }, |
114 | | { "UNKNOWN_OBJECT", DNP3_DECODER_EVENT_UNKNOWN_OBJECT }, |
115 | | { "TOO_MANY_POINTS", DNP3_DECODER_EVENT_TOO_MANY_POINTS }, |
116 | | { "TOO_MANY_OBJECTS", DNP3_DECODER_EVENT_TOO_MANY_OBJECTS }, |
117 | | { "TOO_LONG_REASSEMBLY", DNP3_DECODER_EVENT_TOO_LONG_REASS }, |
118 | | { NULL, -1 }, |
119 | | }; |
120 | | |
121 | | /* Calculate the next transport sequence number. */ |
122 | 367k | #define NEXT_TH_SEQNO(current) ((current + 1) % DNP3_MAX_TRAN_SEQNO) |
123 | | |
124 | | /* Calculate the next application sequence number. */ |
125 | | #define NEXT_APP_SEQNO(current) ((current + 1) % DNP3_MAX_APP_SEQNO) |
126 | | |
127 | | /* CRC table generated by pycrc - http://github.com/tpircher/pycrc. |
128 | | * - Polynomial: 0x3d65. */ |
129 | | static const uint16_t crc_table[256] = { |
130 | | 0x0000, 0x365e, 0x6cbc, 0x5ae2, 0xd978, 0xef26, 0xb5c4, 0x839a, |
131 | | 0xff89, 0xc9d7, 0x9335, 0xa56b, 0x26f1, 0x10af, 0x4a4d, 0x7c13, |
132 | | 0xb26b, 0x8435, 0xded7, 0xe889, 0x6b13, 0x5d4d, 0x07af, 0x31f1, |
133 | | 0x4de2, 0x7bbc, 0x215e, 0x1700, 0x949a, 0xa2c4, 0xf826, 0xce78, |
134 | | 0x29af, 0x1ff1, 0x4513, 0x734d, 0xf0d7, 0xc689, 0x9c6b, 0xaa35, |
135 | | 0xd626, 0xe078, 0xba9a, 0x8cc4, 0x0f5e, 0x3900, 0x63e2, 0x55bc, |
136 | | 0x9bc4, 0xad9a, 0xf778, 0xc126, 0x42bc, 0x74e2, 0x2e00, 0x185e, |
137 | | 0x644d, 0x5213, 0x08f1, 0x3eaf, 0xbd35, 0x8b6b, 0xd189, 0xe7d7, |
138 | | 0x535e, 0x6500, 0x3fe2, 0x09bc, 0x8a26, 0xbc78, 0xe69a, 0xd0c4, |
139 | | 0xacd7, 0x9a89, 0xc06b, 0xf635, 0x75af, 0x43f1, 0x1913, 0x2f4d, |
140 | | 0xe135, 0xd76b, 0x8d89, 0xbbd7, 0x384d, 0x0e13, 0x54f1, 0x62af, |
141 | | 0x1ebc, 0x28e2, 0x7200, 0x445e, 0xc7c4, 0xf19a, 0xab78, 0x9d26, |
142 | | 0x7af1, 0x4caf, 0x164d, 0x2013, 0xa389, 0x95d7, 0xcf35, 0xf96b, |
143 | | 0x8578, 0xb326, 0xe9c4, 0xdf9a, 0x5c00, 0x6a5e, 0x30bc, 0x06e2, |
144 | | 0xc89a, 0xfec4, 0xa426, 0x9278, 0x11e2, 0x27bc, 0x7d5e, 0x4b00, |
145 | | 0x3713, 0x014d, 0x5baf, 0x6df1, 0xee6b, 0xd835, 0x82d7, 0xb489, |
146 | | 0xa6bc, 0x90e2, 0xca00, 0xfc5e, 0x7fc4, 0x499a, 0x1378, 0x2526, |
147 | | 0x5935, 0x6f6b, 0x3589, 0x03d7, 0x804d, 0xb613, 0xecf1, 0xdaaf, |
148 | | 0x14d7, 0x2289, 0x786b, 0x4e35, 0xcdaf, 0xfbf1, 0xa113, 0x974d, |
149 | | 0xeb5e, 0xdd00, 0x87e2, 0xb1bc, 0x3226, 0x0478, 0x5e9a, 0x68c4, |
150 | | 0x8f13, 0xb94d, 0xe3af, 0xd5f1, 0x566b, 0x6035, 0x3ad7, 0x0c89, |
151 | | 0x709a, 0x46c4, 0x1c26, 0x2a78, 0xa9e2, 0x9fbc, 0xc55e, 0xf300, |
152 | | 0x3d78, 0x0b26, 0x51c4, 0x679a, 0xe400, 0xd25e, 0x88bc, 0xbee2, |
153 | | 0xc2f1, 0xf4af, 0xae4d, 0x9813, 0x1b89, 0x2dd7, 0x7735, 0x416b, |
154 | | 0xf5e2, 0xc3bc, 0x995e, 0xaf00, 0x2c9a, 0x1ac4, 0x4026, 0x7678, |
155 | | 0x0a6b, 0x3c35, 0x66d7, 0x5089, 0xd313, 0xe54d, 0xbfaf, 0x89f1, |
156 | | 0x4789, 0x71d7, 0x2b35, 0x1d6b, 0x9ef1, 0xa8af, 0xf24d, 0xc413, |
157 | | 0xb800, 0x8e5e, 0xd4bc, 0xe2e2, 0x6178, 0x5726, 0x0dc4, 0x3b9a, |
158 | | 0xdc4d, 0xea13, 0xb0f1, 0x86af, 0x0535, 0x336b, 0x6989, 0x5fd7, |
159 | | 0x23c4, 0x159a, 0x4f78, 0x7926, 0xfabc, 0xcce2, 0x9600, 0xa05e, |
160 | | 0x6e26, 0x5878, 0x029a, 0x34c4, 0xb75e, 0x8100, 0xdbe2, 0xedbc, |
161 | | 0x91af, 0xa7f1, 0xfd13, 0xcb4d, 0x48d7, 0x7e89, 0x246b, 0x1235 |
162 | | }; |
163 | | |
164 | | /** |
165 | | * \brief Compute the CRC for a buffer. |
166 | | * |
167 | | * \param buf Buffer to create CRC from. |
168 | | * \param len Length of buffer (number of bytes to use for CRC). |
169 | | |
170 | | */ |
171 | | static uint16_t DNP3ComputeCRC(const uint8_t *buf, uint32_t len) |
172 | 0 | { |
173 | 0 | const uint8_t *byte = buf; |
174 | 0 | uint16_t crc = 0; |
175 | 0 | int idx; |
176 | 0 |
|
177 | 0 | while (len--) { |
178 | 0 | idx = (crc ^ *byte) & 0xff; |
179 | 0 | crc = (crc_table[idx] ^ (crc >> 8)) & 0xffff; |
180 | 0 | byte++; |
181 | 0 | } |
182 | 0 |
|
183 | 0 | return ~crc & 0xffff; |
184 | 0 | } |
185 | | |
186 | | /** |
187 | | * \brief Check the CRC of a block. |
188 | | * |
189 | | * \param block The block of data with CRC to be checked. |
190 | | * \param len The size of the data block. |
191 | | * |
192 | | * \retval 1 if CRC is OK, otherwise 0. |
193 | | */ |
194 | | static int DNP3CheckCRC(const uint8_t *block, uint32_t len) |
195 | 12.0M | { |
196 | 12.0M | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
197 | 12.0M | return 1; |
198 | 0 | #endif |
199 | 0 | uint32_t crc_offset; |
200 | 0 | uint16_t crc; |
201 | | |
202 | | /* Need at least one byte plus the CRC. */ |
203 | 0 | if (len < DNP3_CRC_LEN + 1) { |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | 0 | crc_offset = len - DNP3_CRC_LEN; |
208 | 0 | crc = DNP3ComputeCRC(block, len - DNP3_CRC_LEN); |
209 | 0 | if (((crc & 0xff) == block[crc_offset]) && |
210 | 0 | ((crc >> 8) == block[crc_offset + 1])) { |
211 | 0 | return 1; |
212 | 0 | } |
213 | | |
214 | 0 | return 0; |
215 | 0 | } |
216 | | |
217 | | /** |
218 | | * \brief Check the CRC of the link header. |
219 | | * |
220 | | * \param header Point to the link header. |
221 | | * |
222 | | * \retval 1 if header CRC is OK, otherwise 0. |
223 | | */ |
224 | | static int DNP3CheckLinkHeaderCRC(const DNP3LinkHeader *header) |
225 | 4.45M | { |
226 | 4.45M | return DNP3CheckCRC((uint8_t *)header, sizeof(DNP3LinkHeader)); |
227 | 4.45M | } |
228 | | |
229 | | /** |
230 | | * \brief Check user data CRCs. |
231 | | * |
232 | | * \param data Pointer to user data. |
233 | | * \param len Length of user data. |
234 | | * |
235 | | * \retval 1 if CRCs are OK, otherwise 0. |
236 | | */ |
237 | | static int DNP3CheckUserDataCRCs(const uint8_t *data, uint32_t len) |
238 | 4.41M | { |
239 | 4.41M | uint32_t offset = 0; |
240 | 4.41M | uint32_t block_size; |
241 | | |
242 | 12.0M | while (offset < len) { |
243 | 7.63M | if (len - offset >= DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { |
244 | 3.39M | block_size = DNP3_BLOCK_SIZE + DNP3_CRC_LEN; |
245 | 3.39M | } |
246 | 4.23M | else { |
247 | 4.23M | block_size = len - offset; |
248 | 4.23M | } |
249 | | |
250 | 7.63M | if (!DNP3CheckCRC(data + offset, block_size)) { |
251 | | /* Once failed, may as well return immediately. */ |
252 | 0 | return 0; |
253 | 0 | } |
254 | | |
255 | 7.63M | offset += block_size; |
256 | 7.63M | } |
257 | | |
258 | 4.41M | return 1; |
259 | 4.41M | } |
260 | | |
261 | | /** |
262 | | * \brief Check the DNP3 frame start bytes. |
263 | | * |
264 | | * \retval 1 if valid, 0 if not. |
265 | | */ |
266 | | static int DNP3CheckStartBytes(const DNP3LinkHeader *header) |
267 | 4.53M | { |
268 | 4.53M | return header->start_byte0 == DNP3_START_BYTE0 && |
269 | 4.50M | header->start_byte1 == DNP3_START_BYTE1; |
270 | 4.53M | } |
271 | | |
272 | | /* Some DNP3 servers start with a banner. */ |
273 | 648k | #define DNP3_BANNER "DNP3" |
274 | | |
275 | | /** |
276 | | * \brief Check if a frame contains a banner. |
277 | | * |
278 | | * Some servers (outstations) appear to send back a banner that fails |
279 | | * the normal frame checks. So first check for a banner. |
280 | | * |
281 | | * \retval 1 if a banner is found, 0 if not. |
282 | | */ |
283 | | static int DNP3ContainsBanner(const uint8_t *input, uint32_t len) |
284 | 324k | { |
285 | 324k | return BasicSearch(input, len, (uint8_t *)DNP3_BANNER, strlen(DNP3_BANNER)) != NULL; |
286 | 324k | } |
287 | | |
288 | | /** |
289 | | * \brief DNP3 probing parser. |
290 | | */ |
291 | | static uint16_t DNP3ProbingParser(Flow *f, uint8_t direction, |
292 | | const uint8_t *input, uint32_t len, |
293 | | uint8_t *rdir) |
294 | 95.4k | { |
295 | 95.4k | const DNP3LinkHeader *const hdr = (const DNP3LinkHeader *)input; |
296 | 95.4k | const bool toserver = (direction & STREAM_TOSERVER) != 0; |
297 | | |
298 | | /* May be a banner. */ |
299 | 95.4k | if (DNP3ContainsBanner(input, len)) { |
300 | 2.93k | SCLogDebug("Packet contains a DNP3 banner."); |
301 | 2.93k | bool is_banner = true; |
302 | | // magic 0x100 = 256 seems good enough |
303 | 306k | for (uint32_t i = 0; i < len && i < 0x100; i++) { |
304 | 304k | if (!isprint(input[i])) { |
305 | 975 | is_banner = false; |
306 | 975 | break; |
307 | 975 | } |
308 | 304k | } |
309 | 2.93k | if (is_banner) { |
310 | 1.95k | if (toserver) { |
311 | 505 | *rdir = STREAM_TOCLIENT; |
312 | 505 | } |
313 | 1.95k | return ALPROTO_DNP3; |
314 | 1.95k | } |
315 | 2.93k | } |
316 | | |
317 | | /* Check that we have the minimum amount of bytes. */ |
318 | 93.4k | if (len < sizeof(DNP3LinkHeader)) { |
319 | 27.2k | SCLogDebug("Length too small to be a DNP3 header."); |
320 | 27.2k | return ALPROTO_UNKNOWN; |
321 | 27.2k | } |
322 | | |
323 | | /* Verify start value (from AN2013-004b). */ |
324 | 66.2k | if (!DNP3CheckStartBytes(hdr)) { |
325 | 14.5k | SCLogDebug("Invalid start bytes."); |
326 | 14.5k | return ALPROTO_FAILED; |
327 | 14.5k | } |
328 | | |
329 | | /* Verify minimum length. */ |
330 | 51.6k | if (hdr->len < DNP3_MIN_LEN) { |
331 | 481 | SCLogDebug("Packet too small to be a valid DNP3 fragment."); |
332 | 481 | return ALPROTO_FAILED; |
333 | 481 | } |
334 | | |
335 | | // Test compatibility between direction and dnp3.ctl.direction |
336 | 51.1k | if ((DNP3_LINK_DIR(hdr->control) != 0) != toserver) { |
337 | 19.2k | *rdir = toserver ? STREAM_TOCLIENT : STREAM_TOSERVER; |
338 | 19.2k | } |
339 | 51.1k | SCLogDebug("Detected DNP3."); |
340 | 51.1k | return ALPROTO_DNP3; |
341 | 51.6k | } |
342 | | |
343 | | /** |
344 | | * \brief Calculate the length of the transport layer with CRCs removed. |
345 | | * |
346 | | * \param input_len The length of the transport layer buffer. |
347 | | * |
348 | | * \retval The length of the buffer after CRCs are removed. |
349 | | */ |
350 | | static int DNP3CalculateTransportLengthWithoutCRCs(uint32_t input_len) |
351 | 2.13M | { |
352 | | /* Too small. */ |
353 | 2.13M | if (input_len < DNP3_CRC_LEN) { |
354 | 0 | return -1; |
355 | 0 | } |
356 | | |
357 | | /* Get the number of complete blocks. */ |
358 | 2.13M | int blocks = input_len / (DNP3_BLOCK_SIZE + DNP3_CRC_LEN); |
359 | | |
360 | | /* And the number of bytes in the last block. */ |
361 | 2.13M | int rem = input_len - (blocks * (DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); |
362 | | |
363 | 2.13M | if (rem) { |
364 | 2.04M | if (rem < DNP3_CRC_LEN) { |
365 | 0 | return -1; |
366 | 0 | } |
367 | 2.04M | return (blocks * DNP3_BLOCK_SIZE) + (rem - DNP3_CRC_LEN); |
368 | 2.04M | } |
369 | 88.0k | else { |
370 | 88.0k | return (blocks * DNP3_BLOCK_SIZE); |
371 | 88.0k | } |
372 | 2.13M | } |
373 | | |
374 | | /** |
375 | | * \brief Reassemble the application layer by stripping the CRCs. |
376 | | * |
377 | | * Remove the CRCs from the user data blocks. The output is the user |
378 | | * data with the CRCs removed as well as the transport header removed, |
379 | | * but the input data still needs to include the transport header as |
380 | | * its part of the first user data block. |
381 | | * |
382 | | * If the output length passed in is non-null, the new input data will |
383 | | * be appended, and the output length pointer incremented as needed. |
384 | | * |
385 | | * \param input Input buffer starting at the transport header (which |
386 | | * will be removed from the output). |
387 | | * \param input_len Length of the input buffer. |
388 | | * \param output Pointer to output buffer (may be realloc'd). |
389 | | * \param output_len Pointer to output length. |
390 | | * |
391 | | * \retval 1 if reassembly was successful, otherwise 0. |
392 | | */ |
393 | | static int DNP3ReassembleApplicationLayer(const uint8_t *input, |
394 | | uint32_t input_len, uint8_t **output, uint32_t *output_len) |
395 | 2.13M | { |
396 | 2.13M | int len = DNP3CalculateTransportLengthWithoutCRCs(input_len); |
397 | | |
398 | 2.13M | if (len <= 0) { |
399 | 0 | return 0; |
400 | 0 | } |
401 | | |
402 | | /* Remove one byte for the transport header and make sure we have |
403 | | * at least one byte of user data. */ |
404 | 2.13M | if (--len < 1) { |
405 | 0 | return 0; |
406 | 0 | } |
407 | | |
408 | 2.13M | if (*output == NULL) { |
409 | 2.12M | *output = SCCalloc(1, len); |
410 | 2.12M | if (unlikely(*output == NULL)) { |
411 | 0 | return 0; |
412 | 0 | } |
413 | 2.12M | } |
414 | 7.74k | else { |
415 | 7.74k | uint8_t *ptr = SCRealloc(*output, (size_t)(*output_len + len)); |
416 | 7.74k | if (unlikely(ptr == NULL)) { |
417 | 0 | return 0; |
418 | 0 | } |
419 | 7.74k | *output = ptr; |
420 | 7.74k | } |
421 | | |
422 | 2.13M | int offset = 0, block_size; |
423 | 5.80M | while ((uint32_t)offset < input_len) { |
424 | 3.67M | if (input_len - offset > DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { |
425 | 1.53M | block_size = DNP3_BLOCK_SIZE + DNP3_CRC_LEN; |
426 | 1.53M | } |
427 | 2.13M | else { |
428 | 2.13M | block_size = input_len - offset; |
429 | 2.13M | } |
430 | | |
431 | | /* If handling the first block (offset is 0), trim off the |
432 | | * first byte which is the transport header, and not part of |
433 | | * the application data. */ |
434 | 3.67M | if (offset == 0) { |
435 | 2.13M | offset++; |
436 | 2.13M | block_size--; |
437 | 2.13M | } |
438 | | |
439 | | /* Need at least 3 bytes to continue. One for application |
440 | | * data, and 2 for the CRC. If not, return failure for |
441 | | * malformed frame. */ |
442 | 3.67M | if (block_size < DNP3_CRC_LEN + 1) { |
443 | 0 | SCLogDebug("Not enough data to continue."); |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | /* Make sure there is enough space to write into. */ |
448 | 3.67M | if (block_size - DNP3_CRC_LEN > len) { |
449 | 0 | SCLogDebug("Not enough data to continue."); |
450 | 0 | return 0; |
451 | 0 | } |
452 | | |
453 | 3.67M | memcpy(*output + *output_len, input + offset, |
454 | 3.67M | block_size - DNP3_CRC_LEN); |
455 | 3.67M | *output_len += block_size - DNP3_CRC_LEN; |
456 | 3.67M | offset += block_size; |
457 | 3.67M | len -= block_size - DNP3_CRC_LEN; |
458 | 3.67M | } |
459 | | |
460 | 2.13M | return 1; |
461 | 2.13M | } |
462 | | |
463 | | /** |
464 | | * \brief Allocate a DNP3 state object. |
465 | | * |
466 | | * The DNP3 state object represents a single DNP3 TCP session. |
467 | | */ |
468 | | static void *DNP3StateAlloc(void *orig_state, AppProto proto_orig) |
469 | 88.7k | { |
470 | 88.7k | SCEnter(); |
471 | 88.7k | DNP3State *dnp3; |
472 | | |
473 | 88.7k | dnp3 = (DNP3State *)SCCalloc(1, sizeof(DNP3State)); |
474 | 88.7k | if (unlikely(dnp3 == NULL)) { |
475 | 0 | return NULL; |
476 | 0 | } |
477 | 88.7k | TAILQ_INIT(&dnp3->tx_list); |
478 | | |
479 | 88.7k | SCReturnPtr(dnp3, "void"); |
480 | 88.7k | } |
481 | | |
482 | | /** |
483 | | * \brief Set a DNP3 application layer event. |
484 | | * |
485 | | * Sets an event on the current transaction object. |
486 | | */ |
487 | | static void DNP3SetEvent(DNP3State *dnp3, uint8_t event) |
488 | 1.72M | { |
489 | 1.72M | if (dnp3 && dnp3->curr) { |
490 | 1.47M | AppLayerDecoderEventsSetEventRaw(&dnp3->curr->tx_data.events, event); |
491 | 1.47M | dnp3->events++; |
492 | 1.47M | } |
493 | 253k | else { |
494 | 253k | SCLogWarning("Failed to set event, state or tx pointer was NULL."); |
495 | 253k | } |
496 | 1.72M | } |
497 | | |
498 | | /** |
499 | | * \brief Set a DNP3 application layer event on a transaction. |
500 | | */ |
501 | | static void DNP3SetEventTx(DNP3Transaction *tx, uint8_t event) |
502 | 828k | { |
503 | 828k | AppLayerDecoderEventsSetEventRaw(&tx->tx_data.events, event); |
504 | 828k | tx->dnp3->events++; |
505 | 828k | } |
506 | | |
507 | | /** |
508 | | * \brief Allocation a DNP3 transaction. |
509 | | */ |
510 | | static DNP3Transaction *DNP3TxAlloc(DNP3State *dnp3, bool request) |
511 | 2.12M | { |
512 | 2.12M | DNP3Transaction *tx = SCCalloc(1, sizeof(DNP3Transaction)); |
513 | 2.12M | if (unlikely(tx == NULL)) { |
514 | 0 | return NULL; |
515 | 0 | } |
516 | 2.12M | dnp3->transaction_max++; |
517 | 2.12M | dnp3->unreplied++; |
518 | 2.12M | dnp3->curr = tx; |
519 | 2.12M | tx->dnp3 = dnp3; |
520 | 2.12M | tx->tx_num = dnp3->transaction_max; |
521 | 2.12M | tx->is_request = request; |
522 | 2.12M | if (tx->is_request) { |
523 | 1.75M | tx->tx_data.detect_flags_tc |= APP_LAYER_TX_SKIP_INSPECT_FLAG; |
524 | 1.75M | } else { |
525 | 369k | tx->tx_data.detect_flags_ts |= APP_LAYER_TX_SKIP_INSPECT_FLAG; |
526 | 369k | } |
527 | 2.12M | TAILQ_INIT(&tx->objects); |
528 | 2.12M | TAILQ_INSERT_TAIL(&dnp3->tx_list, tx, next); |
529 | | |
530 | | /* Check for flood state. */ |
531 | 2.12M | if (dnp3->unreplied > dnp3_max_tx && !dnp3->flooded) { |
532 | 4.37k | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_FLOODED); |
533 | 4.37k | dnp3->flooded = 1; |
534 | 4.37k | } |
535 | | |
536 | 2.12M | return tx; |
537 | 2.12M | } |
538 | | |
539 | | /** |
540 | | * \brief Calculate the length of a link frame with CRCs. |
541 | | * |
542 | | * This is required as the length parameter in the DNP3 header does not |
543 | | * include the added CRCs. |
544 | | * |
545 | | * \param length The length from the DNP3 link header. |
546 | | * |
547 | | * \retval The length of the frame with CRCs included or 0 if the length isn't |
548 | | * long enough to be a valid DNP3 frame. |
549 | | */ |
550 | | static uint32_t DNP3CalculateLinkLength(uint8_t length) |
551 | 4.45M | { |
552 | 4.45M | uint32_t frame_len = 0; |
553 | 4.45M | int rem; |
554 | | |
555 | | /* Fail early if the length is less than the minimum size. */ |
556 | 4.45M | if (length < DNP3_LINK_HDR_LEN) { |
557 | 212 | return 0; |
558 | 212 | } |
559 | | |
560 | | /* Subtract the 5 bytes of the header that are included in the |
561 | | * length. */ |
562 | 4.45M | length -= DNP3_LINK_HDR_LEN; |
563 | | |
564 | 4.45M | rem = length % DNP3_BLOCK_SIZE; |
565 | 4.45M | frame_len = (length / DNP3_BLOCK_SIZE) * (DNP3_BLOCK_SIZE + DNP3_CRC_LEN); |
566 | 4.45M | if (rem) { |
567 | 2.61M | frame_len += rem + DNP3_CRC_LEN; |
568 | 2.61M | } |
569 | | |
570 | 4.45M | return frame_len + sizeof(DNP3LinkHeader); |
571 | 4.45M | } |
572 | | |
573 | | /** |
574 | | * \brief Check if the link function code specifies user data. |
575 | | * |
576 | | * \param header Point to link header. |
577 | | * |
578 | | * \retval 1 if frame contains user data, otherwise 0. |
579 | | */ |
580 | | static int DNP3IsUserData(const DNP3LinkHeader *header) |
581 | 4.23M | { |
582 | 4.23M | switch (DNP3_LINK_FC(header->control)) { |
583 | 1.07M | case DNP3_LINK_FC_CONFIRMED_USER_DATA: |
584 | 3.93M | case DNP3_LINK_FC_UNCONFIRMED_USER_DATA: |
585 | 3.93M | return 1; |
586 | 302k | default: |
587 | 302k | return 0; |
588 | 4.23M | } |
589 | 4.23M | } |
590 | | |
591 | | /** |
592 | | * \brief Check if the frame has user data. |
593 | | * |
594 | | * Check if the DNP3 frame actually has user data by checking if data |
595 | | * exists after the headers. |
596 | | * |
597 | | * \retval 1 if user data exists, otherwise 0. |
598 | | */ |
599 | | static int DNP3HasUserData(const DNP3LinkHeader *header, uint8_t direction) |
600 | 3.93M | { |
601 | 3.93M | if (direction == STREAM_TOSERVER) { |
602 | 3.51M | return header->len >= DNP3_LINK_HDR_LEN + sizeof(DNP3TransportHeader) + |
603 | 3.51M | sizeof(DNP3ApplicationHeader); |
604 | 3.51M | } |
605 | 413k | else { |
606 | 413k | return header->len >= DNP3_LINK_HDR_LEN + sizeof(DNP3TransportHeader) + |
607 | 413k | sizeof(DNP3ApplicationHeader) + sizeof(DNP3InternalInd); |
608 | 413k | } |
609 | 3.93M | } |
610 | | |
611 | | /** |
612 | | * \brief Reset a DNP3Buffer. |
613 | | */ |
614 | | static void DNP3BufferReset(DNP3Buffer *buffer) |
615 | 187k | { |
616 | 187k | buffer->offset = 0; |
617 | 187k | buffer->len = 0; |
618 | 187k | } |
619 | | |
620 | | /** |
621 | | * \brief Add data to a DNP3 buffer, enlarging the buffer if required. |
622 | | * |
623 | | * \param buffer Buffer to add data data. |
624 | | * \param data Data to be added to buffer. |
625 | | * \param len Size of data to be added to buffer. |
626 | | * |
627 | | * \param 1 if data was added successful, otherwise 0. |
628 | | */ |
629 | | static int DNP3BufferAdd(DNP3Buffer *buffer, const uint8_t *data, uint32_t len) |
630 | 559k | { |
631 | 559k | if (buffer->size == 0) { |
632 | 13.2k | buffer->buffer = SCCalloc(1, len); |
633 | 13.2k | if (unlikely(buffer->buffer == NULL)) { |
634 | 0 | return 0; |
635 | 0 | } |
636 | 13.2k | buffer->size = len; |
637 | 13.2k | } |
638 | 546k | else if (buffer->len + len > buffer->size) { |
639 | 15.2k | uint8_t *tmp = SCRealloc(buffer->buffer, buffer->len + len); |
640 | 15.2k | if (unlikely(tmp == NULL)) { |
641 | 0 | return 0; |
642 | 0 | } |
643 | 15.2k | buffer->buffer = tmp; |
644 | 15.2k | buffer->size = buffer->len + len; |
645 | 15.2k | } |
646 | 559k | memcpy(buffer->buffer + buffer->len, data, len); |
647 | 559k | buffer->len += len; |
648 | | |
649 | 559k | return 1; |
650 | 559k | } |
651 | | |
652 | | /** |
653 | | * \brief Trim a DNP3 buffer. |
654 | | * |
655 | | * Trimming a buffer moves the data in the buffer up to the front of |
656 | | * the buffer freeing up room at the end for more incoming data. |
657 | | * |
658 | | * \param buffer The buffer to trim. |
659 | | */ |
660 | | static void DNP3BufferTrim(DNP3Buffer *buffer) |
661 | 369k | { |
662 | 369k | if (buffer->offset == buffer->len) { |
663 | 176k | DNP3BufferReset(buffer); |
664 | 176k | } |
665 | 193k | else if (buffer->offset > 0) { |
666 | 124k | memmove(buffer->buffer, buffer->buffer + buffer->offset, |
667 | 124k | buffer->len - buffer->offset); |
668 | 124k | buffer->len = buffer->len - buffer->offset; |
669 | 124k | buffer->offset = 0; |
670 | 124k | } |
671 | 369k | } |
672 | | |
673 | | /** |
674 | | * \brief Free a DNP3 object. |
675 | | */ |
676 | | static void DNP3ObjectFree(DNP3Object *object) |
677 | 1.37M | { |
678 | 1.37M | if (object->points != NULL) { |
679 | 1.37M | DNP3FreeObjectPointList(object->group, object->variation, |
680 | 1.37M | object->points); |
681 | 1.37M | } |
682 | 1.37M | SCFree(object); |
683 | 1.37M | } |
684 | | |
685 | | /** |
686 | | * \brief Allocate a DNP3 object. |
687 | | */ |
688 | | static DNP3Object *DNP3ObjectAlloc(void) |
689 | 1.37M | { |
690 | 1.37M | DNP3Object *object = SCCalloc(1, sizeof(*object)); |
691 | 1.37M | if (unlikely(object == NULL)) { |
692 | 0 | return NULL; |
693 | 0 | } |
694 | 1.37M | object->points = DNP3PointListAlloc(); |
695 | 1.37M | if (object->points == NULL) { |
696 | 0 | DNP3ObjectFree(object); |
697 | 0 | return NULL; |
698 | 0 | } |
699 | 1.37M | return object; |
700 | 1.37M | } |
701 | | |
702 | | /** |
703 | | * \brief Decode DNP3 application objects. |
704 | | * |
705 | | * This function decoded known DNP3 application objects. As the |
706 | | * protocol isn't self describing, we can only decode the buffer while |
707 | | * the application objects are known. As soon as an unknown |
708 | | * group/variation is hit, we must stop processing. |
709 | | * |
710 | | * \param buf the input buffer |
711 | | * \param len length of the input buffer |
712 | | * \param objects pointer to list where decoded objects will be stored. |
713 | | * |
714 | | * \retval 1 if all objects decoded, 0 if all objects could not be decoded ( |
715 | | * unknown group/variations) |
716 | | */ |
717 | | static int DNP3DecodeApplicationObjects(DNP3Transaction *tx, const uint8_t *buf, |
718 | | uint32_t len, DNP3ObjectList *objects) |
719 | 1.38M | { |
720 | 1.38M | int retval = 0; |
721 | 1.38M | uint64_t point_count = 0; |
722 | 1.38M | uint64_t object_count = 0; |
723 | | |
724 | 1.38M | if (buf == NULL || len == 0) { |
725 | 238k | return 1; |
726 | 238k | } |
727 | | |
728 | 1.53M | while (len) { |
729 | 1.47M | uint32_t offset = 0; |
730 | | |
731 | 1.47M | if (len < sizeof(DNP3ObjHeader)) { |
732 | 98.9k | goto done; |
733 | 98.9k | } |
734 | 1.37M | DNP3ObjHeader *header = (DNP3ObjHeader *)buf; |
735 | 1.37M | offset += sizeof(DNP3ObjHeader); |
736 | | |
737 | | /* Check if we've exceeded the maximum number of objects. */ |
738 | 1.37M | if (++object_count > dnp3_max_objects) { |
739 | 0 | DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_OBJECTS); |
740 | 0 | goto done; |
741 | 0 | } |
742 | | |
743 | 1.37M | DNP3Object *object = DNP3ObjectAlloc(); |
744 | 1.37M | if (unlikely(object == NULL)) { |
745 | 0 | goto done; |
746 | 0 | } |
747 | 1.37M | TAILQ_INSERT_TAIL(objects, object, next); |
748 | | |
749 | 1.37M | object->group = header->group; |
750 | 1.37M | object->variation = header->variation; |
751 | 1.37M | object->qualifier = header->qualifier; |
752 | 1.37M | object->prefix_code = DNP3_OBJ_PREFIX(header->qualifier); |
753 | 1.37M | object->range_code = DNP3_OBJ_RANGE(header->qualifier); |
754 | | |
755 | | /* IEEE 1815-2012, Table 4-5. */ |
756 | 1.37M | switch (object->range_code) { |
757 | 355k | case 0x00: |
758 | 497k | case 0x03: { |
759 | | /* 1 octet start and stop indexes OR 1 octet start and |
760 | | * stop virtual addresses. */ |
761 | 497k | if (offset + (sizeof(uint8_t) * 2) > len) { |
762 | | /* Not enough data. */ |
763 | 74.4k | SCLogDebug("Not enough data."); |
764 | 74.4k | goto not_enough_data; |
765 | 74.4k | } |
766 | 422k | object->start = buf[offset++]; |
767 | 422k | object->stop = buf[offset++]; |
768 | 422k | object->count = object->stop - object->start + 1; |
769 | 422k | break; |
770 | 497k | } |
771 | 38.7k | case 0x01: |
772 | 150k | case 0x04: { |
773 | | /* 2 octet start and stop indexes OR 2 octect start |
774 | | * and stop virtual addresses. */ |
775 | 150k | if (offset + (sizeof(uint16_t) * 2) > len) { |
776 | | /* Not enough data. */ |
777 | 35.0k | SCLogDebug("Not enough data."); |
778 | 35.0k | goto not_enough_data; |
779 | 35.0k | } |
780 | 115k | object->start = DNP3_SWAP16(*(uint16_t *)(buf + offset)); |
781 | 115k | offset += sizeof(uint16_t); |
782 | 115k | object->stop = DNP3_SWAP16(*(uint16_t *)(buf + offset)); |
783 | 115k | offset += sizeof(uint16_t); |
784 | 115k | object->count = object->stop - object->start + 1; |
785 | 115k | break; |
786 | 150k | } |
787 | 32.9k | case 0x02: |
788 | 49.4k | case 0x05: { |
789 | | /* 4 octet start and stop indexes OR 4 octect start |
790 | | * and stop virtual addresses. */ |
791 | 49.4k | if (offset + (sizeof(uint32_t) * 2) > len) { |
792 | | /* Not enough data. */ |
793 | 4.00k | SCLogDebug("Not enough data."); |
794 | 4.00k | goto not_enough_data; |
795 | 4.00k | } |
796 | 45.4k | object->start = DNP3_SWAP32(*(uint32_t *)(buf + offset)); |
797 | 45.4k | offset += sizeof(uint32_t); |
798 | 45.4k | object->stop = DNP3_SWAP32(*(uint32_t *)(buf + offset)); |
799 | 45.4k | offset += sizeof(uint32_t); |
800 | 45.4k | object->count = object->stop - object->start + 1; |
801 | 45.4k | break; |
802 | 49.4k | } |
803 | 127k | case 0x06: |
804 | | /* No range field. */ |
805 | 127k | object->count = 0; |
806 | 127k | break; |
807 | 192k | case 0x07: |
808 | | /* 1 octet count of objects. */ |
809 | 192k | if (offset + sizeof(uint8_t) > len) { |
810 | 389 | SCLogDebug("Not enough data."); |
811 | 389 | goto not_enough_data; |
812 | 389 | } |
813 | 192k | object->count = buf[offset]; |
814 | 192k | offset += sizeof(uint8_t); |
815 | 192k | break; |
816 | 13.7k | case 0x08: { |
817 | | /* 2 octet count of objects. */ |
818 | 13.7k | if (offset + sizeof(uint16_t) > len) { |
819 | 1.37k | SCLogDebug("Not enough data."); |
820 | 1.37k | goto not_enough_data; |
821 | 1.37k | } |
822 | 12.3k | object->count = DNP3_SWAP16(*(uint16_t *)(buf + offset)); |
823 | 12.3k | offset += sizeof(uint16_t); |
824 | 12.3k | break; |
825 | 13.7k | } |
826 | 9.10k | case 0x09: { |
827 | | /* 4 octet count of objects. */ |
828 | 9.10k | if (offset + sizeof(uint32_t) > len) { |
829 | 835 | SCLogDebug("Not enough data."); |
830 | 835 | goto not_enough_data; |
831 | 835 | } |
832 | 8.26k | object->count = DNP3_SWAP32(*(uint32_t *)(buf + offset)); |
833 | 8.26k | offset += sizeof(uint32_t); |
834 | 8.26k | break; |
835 | 9.10k | } |
836 | 298k | case 0x0b: { |
837 | 298k | if (offset + sizeof(uint8_t) > len) { |
838 | | /* Not enough data. */ |
839 | 746 | SCLogDebug("Not enough data."); |
840 | 746 | goto not_enough_data; |
841 | 746 | } |
842 | 297k | object->count = *(uint8_t *)(buf + offset); |
843 | 297k | offset += sizeof(uint8_t); |
844 | 297k | break; |
845 | 298k | } |
846 | 39.5k | default: |
847 | 39.5k | SCLogDebug("Range code 0x%02x is reserved.", |
848 | 39.5k | object->range_code); |
849 | 39.5k | goto done; |
850 | 1.37M | } |
851 | | |
852 | 1.22M | buf += offset; |
853 | 1.22M | len -= offset; |
854 | | |
855 | 1.22M | if (object->variation == 0 || object->count == 0) { |
856 | 165k | goto next; |
857 | 165k | } |
858 | | |
859 | | /* Check if we've exceeded the maximum number of points per message. */ |
860 | 1.05M | point_count += object->count; |
861 | 1.05M | if (point_count > max_points) { |
862 | 131k | DNP3SetEventTx(tx, DNP3_DECODER_EVENT_TOO_MANY_POINTS); |
863 | 131k | goto done; |
864 | 131k | } |
865 | | |
866 | 924k | int event = DNP3DecodeObject(header->group, header->variation, &buf, |
867 | 924k | &len, object->prefix_code, object->start, object->count, |
868 | 924k | object->points); |
869 | 924k | if (event) { |
870 | 696k | DNP3SetEventTx(tx, DNP3_DECODER_EVENT_UNKNOWN_OBJECT); |
871 | 696k | goto done; |
872 | 696k | } |
873 | | |
874 | 393k | next: |
875 | 393k | continue; |
876 | 924k | } |
877 | | |
878 | | /* All objects were decoded. */ |
879 | 61.0k | retval = 1; |
880 | | |
881 | 177k | not_enough_data: |
882 | 1.14M | done: |
883 | 1.14M | return retval; |
884 | 177k | } |
885 | | |
886 | | /** |
887 | | * \brief Handle DNP3 request user data. |
888 | | * |
889 | | * \param dnp3 the current DNP3State |
890 | | * \param input pointer to the DNP3 frame (starting with link header) |
891 | | * \param input_len length of the input frame |
892 | | */ |
893 | | static void DNP3HandleUserDataRequest(DNP3State *dnp3, const uint8_t *input, |
894 | | uint32_t input_len) |
895 | 842k | { |
896 | 842k | DNP3LinkHeader *lh; |
897 | 842k | DNP3TransportHeader th; |
898 | 842k | DNP3ApplicationHeader *ah; |
899 | 842k | DNP3Transaction *tx = NULL, *ttx; |
900 | | |
901 | 842k | lh = (DNP3LinkHeader *)input; |
902 | | |
903 | 842k | if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), |
904 | 842k | input_len - sizeof(DNP3LinkHeader))) { |
905 | 0 | return; |
906 | 0 | } |
907 | | |
908 | 842k | th = input[sizeof(DNP3LinkHeader)]; |
909 | | |
910 | 842k | if (!DNP3_TH_FIR(th)) { |
911 | 2.32M | TAILQ_FOREACH(ttx, &dnp3->tx_list, next) { |
912 | 2.32M | if (ttx->lh.src == lh->src && ttx->lh.dst == lh->dst && ttx->is_request && !ttx->done && |
913 | 291k | NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->th)) == DNP3_TH_SEQ(th)) { |
914 | 277 | tx = ttx; |
915 | 277 | break; |
916 | 277 | } |
917 | 2.32M | } |
918 | | |
919 | 9.86k | if (tx == NULL) { |
920 | 9.59k | return; |
921 | 9.59k | } |
922 | | |
923 | | /* Update the saved transport header so subsequent segments |
924 | | * will be matched to this sequence number. */ |
925 | 277 | tx->th = th; |
926 | 277 | tx->tx_data.updated_ts = true; |
927 | 277 | } |
928 | 833k | else { |
929 | 833k | ah = (DNP3ApplicationHeader *)(input + sizeof(DNP3LinkHeader) + |
930 | 833k | sizeof(DNP3TransportHeader)); |
931 | | |
932 | | /* Ignore confirms - for now. */ |
933 | 833k | if (ah->function_code == DNP3_APP_FC_CONFIRM) { |
934 | 5.69k | return; |
935 | 5.69k | } |
936 | | |
937 | | /* Create a transaction. */ |
938 | 827k | tx = DNP3TxAlloc(dnp3, true); |
939 | 827k | if (unlikely(tx == NULL)) { |
940 | 0 | return; |
941 | 0 | } |
942 | 827k | tx->tx_data.updated_ts = true; |
943 | 827k | tx->lh = *lh; |
944 | 827k | tx->th = th; |
945 | 827k | tx->ah = *ah; |
946 | 827k | } |
947 | | |
948 | 827k | if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader), |
949 | 827k | input_len - sizeof(DNP3LinkHeader), &tx->buffer, &tx->buffer_len)) { |
950 | | |
951 | | /* Malformed, set event and mark as done. */ |
952 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED); |
953 | 0 | tx->done = 1; |
954 | 0 | return; |
955 | 0 | } |
956 | | // a data link frame has its size on one byte, |
957 | | // and transport layer has sequence in 0-63 |
958 | 827k | if (tx->buffer_len > 63 * 0xff) { |
959 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_TOO_LONG_REASS); |
960 | 0 | tx->done = 1; |
961 | 0 | return; |
962 | 0 | } |
963 | | |
964 | | /* If this is not the final segment, just return. */ |
965 | 827k | if (!DNP3_TH_FIN(th)) { |
966 | 317k | return; |
967 | 317k | } |
968 | | |
969 | 510k | tx->done = 1; |
970 | | |
971 | 510k | if (DNP3DecodeApplicationObjects(tx, tx->buffer + sizeof(DNP3ApplicationHeader), |
972 | 510k | tx->buffer_len - sizeof(DNP3ApplicationHeader), &tx->objects)) { |
973 | 22.8k | tx->complete = 1; |
974 | 22.8k | } |
975 | 510k | } |
976 | | |
977 | | static void DNP3HandleUserDataResponse(DNP3State *dnp3, const uint8_t *input, |
978 | | uint32_t input_len) |
979 | 154k | { |
980 | 154k | DNP3LinkHeader *lh; |
981 | 154k | DNP3TransportHeader th; |
982 | 154k | DNP3ApplicationHeader *ah; |
983 | 154k | DNP3InternalInd *iin; |
984 | 154k | DNP3Transaction *tx = NULL, *ttx; |
985 | 154k | uint32_t offset = 0; |
986 | | |
987 | 154k | lh = (DNP3LinkHeader *)input; |
988 | 154k | offset += sizeof(DNP3LinkHeader); |
989 | | |
990 | 154k | if (!DNP3CheckUserDataCRCs(input + offset, input_len - offset)) { |
991 | 0 | return; |
992 | 0 | } |
993 | | |
994 | 154k | th = input[offset++]; |
995 | | |
996 | 154k | if (!DNP3_TH_FIR(th)) { |
997 | 1.39M | TAILQ_FOREACH(ttx, &dnp3->tx_list, next) { |
998 | 1.39M | if (ttx->lh.src == lh->src && ttx->lh.dst == lh->dst && !ttx->is_request && |
999 | 232k | !ttx->done && NEXT_TH_SEQNO(DNP3_TH_SEQ(ttx->th)) == DNP3_TH_SEQ(th)) { |
1000 | 2.90k | tx = ttx; |
1001 | 2.90k | break; |
1002 | 2.90k | } |
1003 | 1.39M | } |
1004 | | |
1005 | 21.4k | if (tx == NULL) { |
1006 | 18.5k | return; |
1007 | 18.5k | } |
1008 | | |
1009 | | /* Replace the transport header in the transaction with this |
1010 | | * one in case there are more frames. */ |
1011 | 2.90k | tx->th = th; |
1012 | 2.90k | tx->tx_data.updated_tc = true; |
1013 | 2.90k | } |
1014 | 133k | else { |
1015 | 133k | ah = (DNP3ApplicationHeader *)(input + offset); |
1016 | 133k | offset += sizeof(DNP3ApplicationHeader); |
1017 | 133k | iin = (DNP3InternalInd *)(input + offset); |
1018 | | |
1019 | 133k | tx = DNP3TxAlloc(dnp3, false); |
1020 | 133k | if (unlikely(tx == NULL)) { |
1021 | 0 | return; |
1022 | 0 | } |
1023 | 133k | tx->tx_data.updated_tc = true; |
1024 | 133k | tx->lh = *lh; |
1025 | 133k | tx->th = th; |
1026 | 133k | tx->ah = *ah; |
1027 | 133k | tx->iin = *iin; |
1028 | 133k | } |
1029 | | |
1030 | 136k | BUG_ON(tx->is_request); |
1031 | | |
1032 | 136k | if (!DNP3ReassembleApplicationLayer(input + sizeof(DNP3LinkHeader), |
1033 | 136k | input_len - sizeof(DNP3LinkHeader), &tx->buffer, &tx->buffer_len)) { |
1034 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_MALFORMED); |
1035 | 0 | return; |
1036 | 0 | } |
1037 | | // a data link frame has its size on one byte, |
1038 | | // and transport layer has sequence in 0-63 |
1039 | 136k | if (tx->buffer_len > 63 * 0xff) { |
1040 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_TOO_LONG_REASS); |
1041 | 0 | tx->done = 1; |
1042 | 0 | return; |
1043 | 0 | } |
1044 | | |
1045 | 136k | if (!DNP3_TH_FIN(th)) { |
1046 | 52.6k | return; |
1047 | 52.6k | } |
1048 | | |
1049 | 83.4k | tx->done = 1; |
1050 | | |
1051 | 83.4k | offset = sizeof(DNP3ApplicationHeader) + sizeof(DNP3InternalInd); |
1052 | 83.4k | if (DNP3DecodeApplicationObjects( |
1053 | 83.4k | tx, tx->buffer + offset, tx->buffer_len - offset, &tx->objects)) { |
1054 | 23.3k | tx->complete = 1; |
1055 | 23.3k | } |
1056 | 83.4k | } |
1057 | | |
1058 | | /** |
1059 | | * \brief Decode the DNP3 request link layer. |
1060 | | * |
1061 | | * \retval number of bytes processed or -1 if the data stream does not look |
1062 | | * like DNP3. |
1063 | | */ |
1064 | | static int DNP3HandleRequestLinkLayer(DNP3State *dnp3, const uint8_t *input, |
1065 | | uint32_t input_len) |
1066 | 422k | { |
1067 | 422k | SCEnter(); |
1068 | 422k | uint32_t processed = 0; |
1069 | | |
1070 | 4.09M | while (input_len) { |
1071 | | |
1072 | | /* Need at least enough bytes for a DNP3 header. */ |
1073 | 3.89M | if (input_len < sizeof(DNP3LinkHeader)) { |
1074 | 84.5k | break; |
1075 | 84.5k | } |
1076 | | |
1077 | 3.81M | DNP3LinkHeader *header = (DNP3LinkHeader *)input; |
1078 | | |
1079 | 3.81M | if (!DNP3CheckStartBytes(header)) { |
1080 | 5.54k | goto error; |
1081 | 5.54k | } |
1082 | | |
1083 | 3.80M | if (!DNP3CheckLinkHeaderCRC(header)) { |
1084 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC); |
1085 | 0 | goto error; |
1086 | 0 | } |
1087 | | |
1088 | 3.80M | uint32_t frame_len = DNP3CalculateLinkLength(header->len); |
1089 | 3.80M | if (frame_len == 0) { |
1090 | 66 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); |
1091 | 66 | goto error; |
1092 | 66 | } |
1093 | 3.80M | if (input_len < frame_len) { |
1094 | | /* Insufficient data, just break - will wait for more data. */ |
1095 | 137k | break; |
1096 | 137k | } |
1097 | | |
1098 | | /* Ignore non-user data for now. */ |
1099 | 3.66M | if (!DNP3IsUserData(header)) { |
1100 | 147k | goto next; |
1101 | 147k | } |
1102 | | |
1103 | | /* Make sure the header length is large enough for transport and |
1104 | | * application headers. */ |
1105 | 3.51M | if (!DNP3HasUserData(header, STREAM_TOSERVER)) { |
1106 | 1.72M | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); |
1107 | 1.72M | goto next; |
1108 | 1.72M | } |
1109 | | |
1110 | 1.79M | if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), |
1111 | 1.79M | frame_len - sizeof(DNP3LinkHeader))) { |
1112 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC); |
1113 | 0 | goto next; |
1114 | 0 | } |
1115 | | |
1116 | 1.79M | DNP3HandleUserDataRequest(dnp3, input, frame_len); |
1117 | | |
1118 | 3.66M | next: |
1119 | | /* Advance the input buffer. */ |
1120 | 3.66M | input += frame_len; |
1121 | 3.66M | input_len -= frame_len; |
1122 | 3.66M | processed += frame_len; |
1123 | 3.66M | } |
1124 | | |
1125 | 422k | SCReturnInt(processed); |
1126 | 5.61k | error: |
1127 | | /* Error out. Should only happen if this doesn't look like a DNP3 |
1128 | | * frame. */ |
1129 | 5.61k | SCReturnInt(-1); |
1130 | 422k | } |
1131 | | |
1132 | | /** |
1133 | | * \brief Handle incoming request data. |
1134 | | * |
1135 | | * The actual request PDU parsing is done in |
1136 | | * DNP3HandleRequestLinkLayer. This function takes care of buffering TCP |
1137 | | * date if a segment does not contain a complete frame (or contains |
1138 | | * multiple frames, but not the complete final frame). |
1139 | | */ |
1140 | | static AppLayerResult DNP3ParseRequest(Flow *f, void *state, AppLayerParserState *pstate, |
1141 | | StreamSlice stream_slice, void *local_data) |
1142 | 425k | { |
1143 | 425k | SCEnter(); |
1144 | 425k | DNP3State *dnp3 = (DNP3State *)state; |
1145 | 425k | DNP3Buffer *buffer = &dnp3->request_buffer; |
1146 | 425k | int processed = 0; |
1147 | | |
1148 | 425k | const uint8_t *input = StreamSliceGetData(&stream_slice); |
1149 | 425k | uint32_t input_len = StreamSliceGetDataLen(&stream_slice); |
1150 | | |
1151 | 425k | if (input_len == 0) { |
1152 | 3.13k | SCReturnStruct(APP_LAYER_OK); |
1153 | 3.13k | } |
1154 | | |
1155 | 422k | if (buffer->len) { |
1156 | 211k | if (!DNP3BufferAdd(buffer, input, input_len)) { |
1157 | 0 | goto error; |
1158 | 0 | } |
1159 | 211k | processed = DNP3HandleRequestLinkLayer(dnp3, |
1160 | 211k | buffer->buffer + buffer->offset, |
1161 | 211k | buffer->len - buffer->offset); |
1162 | 211k | if (processed < 0) { |
1163 | 421 | goto error; |
1164 | 421 | } |
1165 | 211k | buffer->offset += processed; |
1166 | 211k | DNP3BufferTrim(buffer); |
1167 | 211k | } |
1168 | 210k | else { |
1169 | 210k | processed = DNP3HandleRequestLinkLayer(dnp3, input, input_len); |
1170 | 210k | if (processed < 0) { |
1171 | 5.18k | SCLogDebug("Failed to process request link layer."); |
1172 | 5.18k | goto error; |
1173 | 5.18k | } |
1174 | | |
1175 | 205k | input += processed; |
1176 | 205k | input_len -= processed; |
1177 | | |
1178 | | /* Not all data was processed, buffer it. */ |
1179 | 205k | if (input_len) { |
1180 | 98.6k | if (!DNP3BufferAdd(buffer, input, input_len)) { |
1181 | 0 | goto error; |
1182 | 0 | } |
1183 | 98.6k | } |
1184 | 205k | } |
1185 | | |
1186 | 422k | SCReturnStruct(APP_LAYER_OK); |
1187 | | |
1188 | 5.61k | error: |
1189 | | /* Reset the buffer. */ |
1190 | 5.61k | DNP3BufferReset(buffer); |
1191 | 5.61k | SCReturnStruct(APP_LAYER_ERROR); |
1192 | 422k | } |
1193 | | |
1194 | | /** |
1195 | | * \brief Decode the DNP3 response link layer. |
1196 | | * |
1197 | | * \retval number of bytes processed or -1 if the data stream does not |
1198 | | * like look DNP3. |
1199 | | */ |
1200 | | static int DNP3HandleResponseLinkLayer(DNP3State *dnp3, const uint8_t *input, |
1201 | | uint32_t input_len) |
1202 | 325k | { |
1203 | 325k | SCEnter(); |
1204 | 325k | uint32_t processed = 0; |
1205 | | |
1206 | 893k | while (input_len) { |
1207 | | |
1208 | | /* Need at least enough bytes for a DNP3 header. */ |
1209 | 733k | if (input_len < sizeof(DNP3LinkHeader)) { |
1210 | 76.4k | break; |
1211 | 76.4k | } |
1212 | | |
1213 | 656k | DNP3LinkHeader *header = (DNP3LinkHeader *)input; |
1214 | | |
1215 | 656k | if (!DNP3CheckStartBytes(header)) { |
1216 | 4.95k | goto error; |
1217 | 4.95k | } |
1218 | | |
1219 | 651k | if (!DNP3CheckLinkHeaderCRC(header)) { |
1220 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC); |
1221 | 0 | goto error; |
1222 | 0 | } |
1223 | | |
1224 | | /* Calculate the number of bytes needed to for this frame. */ |
1225 | 651k | uint32_t frame_len = DNP3CalculateLinkLength(header->len); |
1226 | 651k | if (frame_len == 0) { |
1227 | 146 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); |
1228 | 146 | goto error; |
1229 | 146 | } |
1230 | 651k | if (input_len < frame_len) { |
1231 | | /* Insufficient data, just break - will wait for more data. */ |
1232 | 83.7k | break; |
1233 | 83.7k | } |
1234 | | |
1235 | | /* Only handle user data frames for now. */ |
1236 | 567k | if (!DNP3IsUserData(header)) { |
1237 | 154k | goto next; |
1238 | 154k | } |
1239 | | |
1240 | | /* Make sure the header length is large enough for transport and |
1241 | | * application headers. */ |
1242 | 413k | if (!DNP3HasUserData(header, STREAM_TOCLIENT)) { |
1243 | 63 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL); |
1244 | 63 | goto error; |
1245 | 63 | } |
1246 | | |
1247 | 413k | if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader), |
1248 | 413k | frame_len - sizeof(DNP3LinkHeader))) { |
1249 | 0 | DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC); |
1250 | 0 | goto next; |
1251 | 0 | } |
1252 | | |
1253 | 413k | DNP3HandleUserDataResponse(dnp3, input, frame_len); |
1254 | | |
1255 | 567k | next: |
1256 | | /* Advance the input buffer. */ |
1257 | 567k | input += frame_len; |
1258 | 567k | input_len -= frame_len; |
1259 | 567k | processed += frame_len; |
1260 | 567k | } |
1261 | | |
1262 | 325k | SCReturnInt(processed); |
1263 | 5.16k | error: |
1264 | | /* Error out. Should only happen if the data stream no longer |
1265 | | * looks like DNP3. */ |
1266 | 5.16k | SCReturnInt(-1); |
1267 | 325k | } |
1268 | | |
1269 | | /** |
1270 | | * \brief Parse incoming data. |
1271 | | * |
1272 | | * This is the entry function for DNP3 application layer data. Its |
1273 | | * main responsibility is buffering incoming data that cannot be |
1274 | | * processed. |
1275 | | * |
1276 | | * See DNP3ParseResponsePDUs for DNP3 frame handling. |
1277 | | */ |
1278 | | static AppLayerResult DNP3ParseResponse(Flow *f, void *state, AppLayerParserState *pstate, |
1279 | | StreamSlice stream_slice, void *local_data) |
1280 | 387k | { |
1281 | 387k | SCEnter(); |
1282 | | |
1283 | 387k | DNP3State *dnp3 = (DNP3State *)state; |
1284 | 387k | DNP3Buffer *buffer = &dnp3->response_buffer; |
1285 | 387k | int processed; |
1286 | | |
1287 | 387k | const uint8_t *input = StreamSliceGetData(&stream_slice); |
1288 | 387k | uint32_t input_len = StreamSliceGetDataLen(&stream_slice); |
1289 | | |
1290 | 387k | if (buffer->len) { |
1291 | 158k | if (!DNP3BufferAdd(buffer, input, input_len)) { |
1292 | 0 | goto error; |
1293 | 0 | } |
1294 | 158k | processed = DNP3HandleResponseLinkLayer(dnp3, |
1295 | 158k | buffer->buffer + buffer->offset, |
1296 | 158k | buffer->len - buffer->offset); |
1297 | 158k | if (processed < 0) { |
1298 | 278 | goto error; |
1299 | 278 | } |
1300 | 157k | buffer->offset += processed; |
1301 | 157k | DNP3BufferTrim(buffer); |
1302 | 157k | } |
1303 | 228k | else { |
1304 | | |
1305 | | /* Check if this is a banner, ignore if it is. */ |
1306 | 228k | if (DNP3ContainsBanner(input, input_len)) { |
1307 | 61.6k | goto done; |
1308 | 61.6k | } |
1309 | | |
1310 | 167k | processed = DNP3HandleResponseLinkLayer(dnp3, input, input_len); |
1311 | 167k | if (processed < 0) { |
1312 | 4.88k | goto error; |
1313 | 4.88k | } |
1314 | 162k | input += processed; |
1315 | 162k | input_len -= processed; |
1316 | | |
1317 | | /* Not all data was processed, buffer it. */ |
1318 | 162k | if (input_len) { |
1319 | 90.5k | if (!DNP3BufferAdd(buffer, input, input_len)) { |
1320 | 0 | goto error; |
1321 | 0 | } |
1322 | 90.5k | } |
1323 | 162k | } |
1324 | | |
1325 | 381k | done: |
1326 | 381k | SCReturnStruct(APP_LAYER_OK); |
1327 | | |
1328 | 5.16k | error: |
1329 | | /* An error occurred while processing DNP3 frames. Dump the |
1330 | | * buffer as we can't be assured that they are valid anymore. */ |
1331 | 5.16k | DNP3BufferReset(buffer); |
1332 | 5.16k | SCReturnStruct(APP_LAYER_ERROR); |
1333 | 387k | } |
1334 | | |
1335 | | static void *DNP3GetTx(void *alstate, uint64_t tx_id) |
1336 | 29.4k | { |
1337 | 29.4k | SCEnter(); |
1338 | 29.4k | DNP3State *dnp3 = (DNP3State *)alstate; |
1339 | 29.4k | DNP3Transaction *tx = NULL; |
1340 | 29.4k | uint64_t tx_num = tx_id + 1; |
1341 | | |
1342 | 29.4k | if (dnp3->curr && dnp3->curr->tx_num == (tx_num)) { |
1343 | 19.0k | SCReturnPtr(dnp3->curr, "void"); |
1344 | 19.0k | } |
1345 | | |
1346 | 29.4k | TAILQ_FOREACH(tx, &dnp3->tx_list, next) { |
1347 | 9.30k | if (tx_num != tx->tx_num) { |
1348 | 1.89k | continue; |
1349 | 1.89k | } |
1350 | 9.30k | SCReturnPtr(tx, "void"); |
1351 | 9.30k | } |
1352 | | |
1353 | 10.3k | SCReturnPtr(NULL, "void"); |
1354 | 10.3k | } |
1355 | | |
1356 | | static uint64_t DNP3GetTxCnt(void *state) |
1357 | 2.99M | { |
1358 | 2.99M | SCEnter(); |
1359 | 2.99M | uint64_t count = ((uint64_t)((DNP3State *)state)->transaction_max); |
1360 | 2.99M | SCReturnUInt(count); |
1361 | 2.99M | } |
1362 | | |
1363 | | /** |
1364 | | * \brief Free all the objects in a DNP3ObjectList. |
1365 | | */ |
1366 | | static void DNP3TxFreeObjectList(DNP3ObjectList *objects) |
1367 | 2.12M | { |
1368 | 2.12M | DNP3Object *object; |
1369 | | |
1370 | 3.50M | while ((object = TAILQ_FIRST(objects)) != NULL) { |
1371 | 1.37M | TAILQ_REMOVE(objects, object, next); |
1372 | 1.37M | DNP3ObjectFree(object); |
1373 | 1.37M | } |
1374 | 2.12M | } |
1375 | | |
1376 | | /** |
1377 | | * \brief Free a DNP3 transaction. |
1378 | | */ |
1379 | | static void DNP3TxFree(DNP3Transaction *tx) |
1380 | 960k | { |
1381 | 960k | SCEnter(); |
1382 | | |
1383 | 960k | if (tx->buffer != NULL) { |
1384 | 960k | SCFree(tx->buffer); |
1385 | 960k | } |
1386 | | |
1387 | 960k | AppLayerDecoderEventsFreeEvents(&tx->tx_data.events); |
1388 | | |
1389 | 960k | if (tx->tx_data.de_state != NULL) { |
1390 | 6.09k | DetectEngineStateFree(tx->tx_data.de_state); |
1391 | 6.09k | } |
1392 | | |
1393 | 960k | DNP3TxFreeObjectList(&tx->objects); |
1394 | | |
1395 | 960k | SCFree(tx); |
1396 | 960k | SCReturn; |
1397 | 960k | } |
1398 | | |
1399 | | /** |
1400 | | * \brief Free a transaction by ID on a specific DNP3 state. |
1401 | | * |
1402 | | * This function is called by the app-layer to free a transaction on a |
1403 | | * specific DNP3 state object. |
1404 | | */ |
1405 | | static void DNP3StateTxFree(void *state, uint64_t tx_id) |
1406 | 1.20M | { |
1407 | 1.20M | SCEnter(); |
1408 | 1.20M | DNP3State *dnp3 = state; |
1409 | 1.20M | DNP3Transaction *tx = NULL, *ttx; |
1410 | 1.20M | uint64_t tx_num = tx_id + 1; |
1411 | | |
1412 | 4.20M | TAILQ_FOREACH_SAFE(tx, &dnp3->tx_list, next, ttx) { |
1413 | | |
1414 | 4.20M | if (tx->tx_num != tx_num) { |
1415 | 3.00M | continue; |
1416 | 3.00M | } |
1417 | | |
1418 | 1.20M | if (tx == dnp3->curr) { |
1419 | 305k | dnp3->curr = NULL; |
1420 | 305k | } |
1421 | | |
1422 | 1.20M | if (tx->tx_data.events != NULL) { |
1423 | 156k | if (tx->tx_data.events->cnt <= dnp3->events) { |
1424 | 142k | dnp3->events -= tx->tx_data.events->cnt; |
1425 | 142k | } else { |
1426 | 14.5k | dnp3->events = 0; |
1427 | 14.5k | } |
1428 | 156k | } |
1429 | 1.20M | dnp3->unreplied--; |
1430 | | |
1431 | | /* Check flood state. */ |
1432 | 1.20M | if (dnp3->flooded && dnp3->unreplied < dnp3_max_tx) { |
1433 | 1.31k | dnp3->flooded = 0; |
1434 | 1.31k | } |
1435 | | |
1436 | 1.20M | TAILQ_REMOVE(&dnp3->tx_list, tx, next); |
1437 | 1.20M | DNP3TxFree(tx); |
1438 | 1.20M | break; |
1439 | 4.20M | } |
1440 | | |
1441 | 1.20M | SCReturn; |
1442 | 1.20M | } |
1443 | | |
1444 | | /** |
1445 | | * \brief Free a DNP3 state. |
1446 | | */ |
1447 | | static void DNP3StateFree(void *state) |
1448 | 88.7k | { |
1449 | 88.7k | SCEnter(); |
1450 | 88.7k | DNP3State *dnp3 = state; |
1451 | 88.7k | DNP3Transaction *tx; |
1452 | 88.7k | if (state != NULL) { |
1453 | 1.00M | while ((tx = TAILQ_FIRST(&dnp3->tx_list)) != NULL) { |
1454 | 919k | TAILQ_REMOVE(&dnp3->tx_list, tx, next); |
1455 | 919k | DNP3TxFree(tx); |
1456 | 919k | } |
1457 | 88.7k | if (dnp3->request_buffer.buffer != NULL) { |
1458 | 10.8k | SCFree(dnp3->request_buffer.buffer); |
1459 | 10.8k | } |
1460 | 88.7k | if (dnp3->response_buffer.buffer != NULL) { |
1461 | 2.37k | SCFree(dnp3->response_buffer.buffer); |
1462 | 2.37k | } |
1463 | 88.7k | SCFree(dnp3); |
1464 | 88.7k | } |
1465 | 88.7k | SCReturn; |
1466 | 88.7k | } |
1467 | | |
1468 | | /** |
1469 | | * \brief Called by the app-layer to get the state progress. |
1470 | | */ |
1471 | | static int DNP3GetAlstateProgress(void *tx, uint8_t direction) |
1472 | 12.2M | { |
1473 | 12.2M | DNP3Transaction *dnp3tx = (DNP3Transaction *)tx; |
1474 | 12.2M | DNP3State *dnp3 = dnp3tx->dnp3; |
1475 | 12.2M | int retval = 0; |
1476 | | |
1477 | | /* If flooded, "ack" old transactions. */ |
1478 | 12.2M | if (dnp3->flooded && (dnp3->transaction_max - dnp3tx->tx_num >= dnp3_max_tx)) { |
1479 | 1.70M | SCLogDebug("flooded: returning tx as done."); |
1480 | 1.70M | SCReturnInt(1); |
1481 | 1.70M | } |
1482 | | |
1483 | 10.5M | if (dnp3tx->done) |
1484 | 1.20M | retval = 1; |
1485 | | |
1486 | 10.5M | SCReturnInt(retval); |
1487 | 12.2M | } |
1488 | | |
1489 | | /** |
1490 | | * \brief App-layer support. |
1491 | | */ |
1492 | | static int DNP3StateGetEventInfo(const char *event_name, int *event_id, |
1493 | | AppLayerEventType *event_type) |
1494 | 3.11k | { |
1495 | 3.11k | *event_id = SCMapEnumNameToValue(event_name, dnp3_decoder_event_table); |
1496 | 3.11k | if (*event_id == -1) { |
1497 | 1.23k | SCLogError("Event \"%s\" not present in " |
1498 | 1.23k | "the DNP3 enum event map table.", |
1499 | 1.23k | event_name); |
1500 | 1.23k | return -1; |
1501 | 1.23k | } |
1502 | | |
1503 | 1.87k | *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; |
1504 | | |
1505 | 1.87k | return 0; |
1506 | 3.11k | } |
1507 | | |
1508 | | /** |
1509 | | * \brief App-layer support. |
1510 | | */ |
1511 | | static int DNP3StateGetEventInfoById(int event_id, const char **event_name, |
1512 | | AppLayerEventType *event_type) |
1513 | 15.5k | { |
1514 | 15.5k | *event_name = SCMapEnumValueToName(event_id, dnp3_decoder_event_table); |
1515 | 15.5k | if (*event_name == NULL) { |
1516 | 0 | SCLogError("Event \"%d\" not present in " |
1517 | 0 | "the DNP3 enum event map table.", |
1518 | 0 | event_id); |
1519 | 0 | return -1; |
1520 | 0 | } |
1521 | | |
1522 | 15.5k | *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; |
1523 | | |
1524 | 15.5k | return 0; |
1525 | 15.5k | } |
1526 | | |
1527 | | static AppLayerTxData *DNP3GetTxData(void *vtx) |
1528 | 10.8M | { |
1529 | 10.8M | DNP3Transaction *tx = (DNP3Transaction *)vtx; |
1530 | 10.8M | return &tx->tx_data; |
1531 | 10.8M | } |
1532 | | |
1533 | | static AppLayerStateData *DNP3GetStateData(void *vstate) |
1534 | 56.7k | { |
1535 | 56.7k | DNP3State *state = (DNP3State *)vstate; |
1536 | 56.7k | return &state->state_data; |
1537 | 56.7k | } |
1538 | | |
1539 | | /** |
1540 | | * \brief Check if the prefix code is a size prefix. |
1541 | | * |
1542 | | * \retval 1 if the prefix_code specifies a size prefix, 0 if not. |
1543 | | */ |
1544 | | int DNP3PrefixIsSize(uint8_t prefix_code) |
1545 | 391k | { |
1546 | 391k | switch (prefix_code) { |
1547 | 8.86k | case 0x04: |
1548 | 21.4k | case 0x05: |
1549 | 26.8k | case 0x06: |
1550 | 26.8k | return 1; |
1551 | 0 | break; |
1552 | 364k | default: |
1553 | 364k | return 0; |
1554 | 391k | } |
1555 | 391k | } |
1556 | | |
1557 | | static AppLayerGetTxIterTuple DNP3GetTxIterator(const uint8_t ipproto, const AppProto alproto, |
1558 | | void *alstate, uint64_t min_tx_id, uint64_t max_tx_id, AppLayerGetTxIterState *state) |
1559 | 11.1M | { |
1560 | 11.1M | DNP3State *dnp_state = (DNP3State *)alstate; |
1561 | 11.1M | AppLayerGetTxIterTuple no_tuple = { NULL, 0, false }; |
1562 | 11.1M | if (dnp_state) { |
1563 | 11.1M | DNP3Transaction *tx_ptr; |
1564 | 11.1M | if (state->un.ptr == NULL) { |
1565 | 1.31M | tx_ptr = TAILQ_FIRST(&dnp_state->tx_list); |
1566 | 9.82M | } else { |
1567 | 9.82M | tx_ptr = (DNP3Transaction *)state->un.ptr; |
1568 | 9.82M | } |
1569 | 11.1M | if (tx_ptr) { |
1570 | 11.1M | while (tx_ptr->tx_num < min_tx_id + 1) { |
1571 | 382k | tx_ptr = TAILQ_NEXT(tx_ptr, next); |
1572 | 382k | if (!tx_ptr) { |
1573 | 65.6k | return no_tuple; |
1574 | 65.6k | } |
1575 | 382k | } |
1576 | 10.7M | if (tx_ptr->tx_num >= max_tx_id + 1) { |
1577 | 0 | return no_tuple; |
1578 | 0 | } |
1579 | 10.7M | state->un.ptr = TAILQ_NEXT(tx_ptr, next); |
1580 | 10.7M | AppLayerGetTxIterTuple tuple = { |
1581 | 10.7M | .tx_ptr = tx_ptr, |
1582 | 10.7M | .tx_id = tx_ptr->tx_num - 1, |
1583 | 10.7M | .has_next = (state->un.ptr != NULL), |
1584 | 10.7M | }; |
1585 | 10.7M | return tuple; |
1586 | 10.7M | } |
1587 | 11.1M | } |
1588 | 330k | return no_tuple; |
1589 | 11.1M | } |
1590 | | |
1591 | | /** |
1592 | | * \brief Register the DNP3 application protocol parser. |
1593 | | */ |
1594 | | void RegisterDNP3Parsers(void) |
1595 | 76 | { |
1596 | 76 | SCEnter(); |
1597 | | |
1598 | 76 | const char *proto_name = "dnp3"; |
1599 | | |
1600 | 76 | if (AppLayerProtoDetectConfProtoDetectionEnabledDefault("tcp", proto_name, false)) { |
1601 | 76 | AppLayerProtoDetectRegisterProtocol(ALPROTO_DNP3, proto_name); |
1602 | | |
1603 | 76 | if (RunmodeIsUnittests()) { |
1604 | 0 | AppLayerProtoDetectPPRegister(IPPROTO_TCP, DNP3_DEFAULT_PORT, |
1605 | 0 | ALPROTO_DNP3, 0, sizeof(DNP3LinkHeader), STREAM_TOSERVER, |
1606 | 0 | DNP3ProbingParser, DNP3ProbingParser); |
1607 | 0 | } |
1608 | 76 | else { |
1609 | 76 | if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP, |
1610 | 76 | proto_name, ALPROTO_DNP3, 0, sizeof(DNP3LinkHeader), |
1611 | 76 | DNP3ProbingParser, DNP3ProbingParser)) { |
1612 | 3 | return; |
1613 | 3 | } |
1614 | 76 | } |
1615 | | |
1616 | 76 | } else { |
1617 | 0 | SCLogConfig("Protocol detection and parser disabled for DNP3."); |
1618 | 0 | SCReturn; |
1619 | 0 | } |
1620 | | |
1621 | 73 | if (AppLayerParserConfParserEnabled("tcp", proto_name)) |
1622 | 73 | { |
1623 | 73 | SCLogConfig("Registering DNP3/tcp parsers."); |
1624 | | |
1625 | 73 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNP3, STREAM_TOSERVER, |
1626 | 73 | DNP3ParseRequest); |
1627 | 73 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNP3, STREAM_TOCLIENT, |
1628 | 73 | DNP3ParseResponse); |
1629 | | |
1630 | 73 | AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNP3, |
1631 | 73 | DNP3StateAlloc, DNP3StateFree); |
1632 | | |
1633 | 73 | AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTx); |
1634 | 73 | AppLayerParserRegisterGetTxIterator(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTxIterator); |
1635 | 73 | AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetTxCnt); |
1636 | 73 | AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNP3, |
1637 | 73 | DNP3StateTxFree); |
1638 | | |
1639 | 73 | AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNP3, |
1640 | 73 | DNP3GetAlstateProgress); |
1641 | 73 | AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_DNP3, 1, 1); |
1642 | | |
1643 | 73 | AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNP3, |
1644 | 73 | DNP3StateGetEventInfo); |
1645 | 73 | AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_DNP3, |
1646 | 73 | DNP3StateGetEventInfoById); |
1647 | | |
1648 | 73 | AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_DNP3, |
1649 | 73 | DNP3GetTxData); |
1650 | 73 | AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_DNP3, DNP3GetStateData); |
1651 | | |
1652 | | /* Parse max-tx configuration. */ |
1653 | 73 | intmax_t value = 0; |
1654 | 73 | if (ConfGetInt("app-layer.protocols.dnp3.max-tx", &value)) { |
1655 | 0 | dnp3_max_tx = (uint64_t)value; |
1656 | 0 | } |
1657 | | |
1658 | | /* Parse max-points configuration. */ |
1659 | 73 | if (ConfGetInt("app-layer.protocols.dnp3.max-points", &value)) { |
1660 | 0 | if (value > 0) { |
1661 | 0 | max_points = (uint64_t)value; |
1662 | 0 | } |
1663 | 0 | } |
1664 | | |
1665 | | /* Parse max-objects configuration. */ |
1666 | 73 | if (ConfGetInt("app-layer.protocols.dnp3.max-objects", &value)) { |
1667 | 0 | if (value > 0) { |
1668 | 0 | dnp3_max_objects = (uint64_t)value; |
1669 | 0 | } |
1670 | 0 | } |
1671 | 73 | } else { |
1672 | 0 | SCLogConfig("Parser disabled for protocol %s. " |
1673 | 0 | "Protocol detection still on.", proto_name); |
1674 | 0 | } |
1675 | | |
1676 | | #ifdef UNITTESTS |
1677 | | AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DNP3, |
1678 | | DNP3ParserRegisterTests); |
1679 | | #endif |
1680 | | |
1681 | 73 | SCReturn; |
1682 | 76 | } |
1683 | | |
1684 | | #ifdef UNITTESTS |
1685 | | |
1686 | | #include "flow-util.h" |
1687 | | #include "stream-tcp.h" |
1688 | | |
1689 | | /** |
1690 | | * \brief Utility function to fix CRCs when mangling a frame. |
1691 | | */ |
1692 | | static void DNP3FixCrc(uint8_t *data, uint32_t len) |
1693 | | { |
1694 | | uint32_t block_size; |
1695 | | |
1696 | | while (len) { |
1697 | | if (len >= DNP3_BLOCK_SIZE + DNP3_CRC_LEN) { |
1698 | | block_size = DNP3_BLOCK_SIZE; |
1699 | | } else { |
1700 | | block_size = len - DNP3_CRC_LEN; |
1701 | | } |
1702 | | uint16_t crc = DNP3ComputeCRC(data, block_size); |
1703 | | data[block_size + 1] = (crc >> 8) & 0xff; |
1704 | | data[block_size] = crc & 0xff; |
1705 | | data += block_size + DNP3_CRC_LEN; |
1706 | | len -= block_size + DNP3_CRC_LEN; |
1707 | | } |
1708 | | } |
1709 | | |
1710 | | /** |
1711 | | * \test Test CRC checking on partial and full blocks. |
1712 | | */ |
1713 | | static int DNP3ParserTestCheckCRC(void) |
1714 | | { |
1715 | | uint8_t request[] = { |
1716 | | /* DNP3 start. */ |
1717 | | 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, |
1718 | | 0xa5, 0xe9, |
1719 | | |
1720 | | /* Transport header. */ |
1721 | | 0xff, |
1722 | | |
1723 | | /* Application layer - segment 1. */ |
1724 | | 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, |
1725 | | 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, |
1726 | | 0xef, |
1727 | | |
1728 | | /* Application layer - segment 2. */ |
1729 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff |
1730 | | }; |
1731 | | |
1732 | | /* Check link header CRC. */ |
1733 | | FAIL_IF(!DNP3CheckCRC(request, sizeof(DNP3LinkHeader))); |
1734 | | |
1735 | | /* Check first application layer segment. */ |
1736 | | FAIL_IF(!DNP3CheckCRC(request + sizeof(DNP3LinkHeader), |
1737 | | DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); |
1738 | | |
1739 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1740 | | /* Change a byte in link header, should fail now. */ |
1741 | | request[2]++; |
1742 | | FAIL_IF(DNP3CheckCRC(request, sizeof(DNP3LinkHeader))); |
1743 | | |
1744 | | /* Change a byte in the first application segment, should fail |
1745 | | * now. */ |
1746 | | request[sizeof(DNP3LinkHeader) + 3]++; |
1747 | | FAIL_IF(DNP3CheckCRC(request + sizeof(DNP3LinkHeader), |
1748 | | DNP3_BLOCK_SIZE + DNP3_CRC_LEN)); |
1749 | | #endif |
1750 | | |
1751 | | PASS; |
1752 | | } |
1753 | | |
1754 | | /** |
1755 | | * \test Test validation of all CRCs in user data. |
1756 | | */ |
1757 | | static int DNP3CheckUserDataCRCsTest(void) |
1758 | | { |
1759 | | /* Multi-block data with valid CRCs. */ |
1760 | | uint8_t data_valid[] = { |
1761 | | 0xff, 0xc9, 0x05, 0x0c, |
1762 | | 0x01, 0x28, 0x01, 0x00, |
1763 | | 0x00, 0x00, 0x01, 0x01, |
1764 | | 0x01, 0x00, 0x00, 0x00, |
1765 | | 0x72, 0xef, /* CRC. */ |
1766 | | |
1767 | | 0xff, 0xc9, 0x05, 0x0c, |
1768 | | 0x01, 0x28, 0x01, 0x00, |
1769 | | 0x00, 0x00, 0x01, 0x01, |
1770 | | 0x01, 0x00, 0x00, 0x00, |
1771 | | 0x72, 0xef, /* CRC. */ |
1772 | | |
1773 | | 0xff, 0xc9, 0x05, 0x0c, |
1774 | | 0x01, 0x28, 0x01, 0x00, |
1775 | | 0x00, 0x00, 0x01, 0x01, |
1776 | | 0x01, 0x00, 0x00, 0x00, |
1777 | | 0x72, 0xef, /* CRC. */ |
1778 | | |
1779 | | 0x00, 0x00, 0x00, 0x00, |
1780 | | 0x00, |
1781 | | 0xff, 0xff, /* CRC. */ |
1782 | | }; |
1783 | | FAIL_IF(!DNP3CheckUserDataCRCs(data_valid, sizeof(data_valid))); |
1784 | | |
1785 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1786 | | /* Multi-block data with one non-crc byte altered. */ |
1787 | | uint8_t data_invalid[] = { |
1788 | | 0xff, 0xc9, 0x05, 0x0c, |
1789 | | 0x01, 0x28, 0x01, 0x00, |
1790 | | 0x00, 0x00, 0x01, 0x01, |
1791 | | 0x01, 0x00, 0x00, 0x00, |
1792 | | 0x72, 0xef, /* CRC. */ |
1793 | | |
1794 | | 0xff, 0xc9, 0x05, 0x0c, |
1795 | | 0x01, 0x28, 0x01, 0x00, |
1796 | | 0x00, 0x00, 0x01, 0x01, |
1797 | | 0x01, 0x00, 0x00, 0x00, |
1798 | | 0x72, 0xef, /* CRC. */ |
1799 | | |
1800 | | 0xff, 0xc9, 0x05, 0x0c, |
1801 | | 0x01, 0x28, 0x01, 0x00, |
1802 | | 0x00, 0x00, 0x01, 0x01, |
1803 | | 0x01, 0x00, 0x00, 0x00, |
1804 | | 0x72, 0xef, /* CRC. */ |
1805 | | |
1806 | | 0x00, 0x00, 0x00, 0x00, |
1807 | | 0x01, /* Invalid byte. */ |
1808 | | 0xff, 0xff, /* CRC. */ |
1809 | | }; |
1810 | | FAIL_IF(DNP3CheckUserDataCRCs(data_invalid, sizeof(data_invalid))); |
1811 | | |
1812 | | /* 1 byte - need at least 3. */ |
1813 | | uint8_t one_byte_nocrc[] = { 0x01 }; |
1814 | | FAIL_IF(DNP3CheckUserDataCRCs(one_byte_nocrc, sizeof(one_byte_nocrc))); |
1815 | | |
1816 | | /* 2 bytes - need at least 3. */ |
1817 | | uint8_t two_byte_nocrc[] = { 0x01, 0x02 }; |
1818 | | FAIL_IF(DNP3CheckUserDataCRCs(two_byte_nocrc, sizeof(two_byte_nocrc))); |
1819 | | #endif |
1820 | | |
1821 | | /* 3 bytes, valid CRC. */ |
1822 | | uint8_t three_bytes_good_crc[] = { 0x00, 0x00, 0x00 }; |
1823 | | *(uint16_t *)(three_bytes_good_crc + 1) = DNP3ComputeCRC( |
1824 | | three_bytes_good_crc, 1); |
1825 | | FAIL_IF(!DNP3CheckUserDataCRCs(three_bytes_good_crc, |
1826 | | sizeof(three_bytes_good_crc))); |
1827 | | |
1828 | | PASS; |
1829 | | } |
1830 | | |
1831 | | /** |
1832 | | * \test Test the link layer length calculation. |
1833 | | * |
1834 | | * Test the calculation that converts the link provided in the DNP3 |
1835 | | * header to the actual length of the frame. That is the length with |
1836 | | * CRCs as the length in the header does not include CRCs. |
1837 | | */ |
1838 | | static int DNP3CalculateLinkLengthTest(void) |
1839 | | { |
1840 | | /* These are invalid. */ |
1841 | | FAIL_IF(DNP3CalculateLinkLength(0) != 0); |
1842 | | FAIL_IF(DNP3CalculateLinkLength(1) != 0); |
1843 | | FAIL_IF(DNP3CalculateLinkLength(2) != 0); |
1844 | | FAIL_IF(DNP3CalculateLinkLength(3) != 0); |
1845 | | FAIL_IF(DNP3CalculateLinkLength(4) != 0); |
1846 | | |
1847 | | /* This is the minimum size. */ |
1848 | | FAIL_IF(DNP3CalculateLinkLength(5) != 10); |
1849 | | |
1850 | | /* 1 full user data blocks of data. */ |
1851 | | FAIL_IF(DNP3CalculateLinkLength(21) != 28); |
1852 | | |
1853 | | /* 2 full user data blocks of data. */ |
1854 | | FAIL_IF(DNP3CalculateLinkLength(37) != 46); |
1855 | | |
1856 | | /* 2 full user data blocks, plus one more byte. */ |
1857 | | /* 2 full user data blocks of data. */ |
1858 | | FAIL_IF(DNP3CalculateLinkLength(38) != 49); |
1859 | | |
1860 | | /* The maximum size. */ |
1861 | | FAIL_IF(DNP3CalculateLinkLength(255) != 292); |
1862 | | |
1863 | | PASS; |
1864 | | } |
1865 | | |
1866 | | /** |
1867 | | * \test The conversion of length with CRCs to the length without |
1868 | | * CRCs. |
1869 | | */ |
1870 | | static int DNP3CalculateTransportLengthWithoutCRCsTest(void) |
1871 | | { |
1872 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(0) != -1); |
1873 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(1) != -1); |
1874 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(2) != 0); |
1875 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(3) != 1); |
1876 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(16) != 14); |
1877 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(17) != 15); |
1878 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(18) != 16); |
1879 | | |
1880 | | /* 19 bytes is not enough for a second block. */ |
1881 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(19) != -1); |
1882 | | |
1883 | | /* 20 bytes really isn't enough either, but is large enough to |
1884 | | * satisfy the CRC on the second block. */ |
1885 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(20) != 16); |
1886 | | |
1887 | | FAIL_IF(DNP3CalculateTransportLengthWithoutCRCs(21) != 17); |
1888 | | |
1889 | | PASS; |
1890 | | } |
1891 | | |
1892 | | /** |
1893 | | * \test Test the validation of the link header CRC. |
1894 | | */ |
1895 | | static int DNP3ParserCheckLinkHeaderCRC(void) |
1896 | | { |
1897 | | /* DNP3 frame with valid headers and CRCs. */ |
1898 | | uint8_t request[] = { |
1899 | | /* DNP3 start. */ |
1900 | | 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, |
1901 | | 0xa5, 0xe9, |
1902 | | |
1903 | | /* Transport header. */ |
1904 | | 0xff, |
1905 | | |
1906 | | /* Application layer. */ |
1907 | | 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, |
1908 | | 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, |
1909 | | 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff |
1910 | | }; |
1911 | | |
1912 | | DNP3LinkHeader *header = (DNP3LinkHeader *)request; |
1913 | | FAIL_IF(!DNP3CheckLinkHeaderCRC(header)); |
1914 | | |
1915 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1916 | | /* Alter a byte in the header. */ |
1917 | | request[4] = 0; |
1918 | | FAIL_IF(DNP3CheckLinkHeaderCRC(header)); |
1919 | | #endif |
1920 | | |
1921 | | PASS; |
1922 | | } |
1923 | | |
1924 | | /** |
1925 | | * \test Test removal of CRCs from user data. |
1926 | | */ |
1927 | | static int DNP3ReassembleApplicationLayerTest01(void) |
1928 | | { |
1929 | | uint32_t reassembled_len = 0; |
1930 | | uint8_t *output = NULL; |
1931 | | |
1932 | | uint8_t payload[] = { |
1933 | | |
1934 | | 0xff, 0xc9, 0x05, 0x0c, |
1935 | | 0x01, 0x28, 0x01, 0x00, |
1936 | | 0x00, 0x00, 0x01, 0x01, |
1937 | | 0x01, 0x00, 0x00, 0x00, |
1938 | | 0x72, 0xef, /* CRC. */ |
1939 | | |
1940 | | 0xff, 0xc9, 0x05, 0x0c, |
1941 | | 0x01, 0x28, 0x01, 0x00, |
1942 | | 0x00, 0x00, 0x01, 0x01, |
1943 | | 0x01, 0x00, 0x00, 0x00, |
1944 | | 0x72, 0xef, /* CRC. */ |
1945 | | |
1946 | | 0xff, 0xc9, 0x05, 0x0c, |
1947 | | 0x01, 0x28, 0x01, 0x00, |
1948 | | 0x00, 0x00, 0x01, 0x01, |
1949 | | 0x01, 0x00, 0x00, 0x00, |
1950 | | 0x72, 0xef, /* CRC. */ |
1951 | | |
1952 | | 0x00, 0x00, 0x00, 0x00, |
1953 | | 0x00, |
1954 | | 0xff, 0xff, /* CRC. */ |
1955 | | }; |
1956 | | |
1957 | | uint8_t expected[] = { |
1958 | | 0xc9, 0x05, 0x0c, |
1959 | | 0x01, 0x28, 0x01, 0x00, |
1960 | | 0x00, 0x00, 0x01, 0x01, |
1961 | | 0x01, 0x00, 0x00, 0x00, |
1962 | | /* CRC removed. */ |
1963 | | 0xff, 0xc9, 0x05, 0x0c, |
1964 | | 0x01, 0x28, 0x01, 0x00, |
1965 | | 0x00, 0x00, 0x01, 0x01, |
1966 | | 0x01, 0x00, 0x00, 0x00, |
1967 | | /* CRC removed. */ |
1968 | | 0xff, 0xc9, 0x05, 0x0c, |
1969 | | 0x01, 0x28, 0x01, 0x00, |
1970 | | 0x00, 0x00, 0x01, 0x01, |
1971 | | 0x01, 0x00, 0x00, 0x00, |
1972 | | /* CRC removed. */ |
1973 | | 0x00, 0x00, 0x00, 0x00, |
1974 | | 0x00 |
1975 | | /* CRC removed. */ |
1976 | | }; |
1977 | | |
1978 | | /* Valid frame. */ |
1979 | | FAIL_IF(!DNP3ReassembleApplicationLayer(payload, |
1980 | | sizeof(payload), &output, &reassembled_len)); |
1981 | | FAIL_IF(output == NULL); |
1982 | | FAIL_IF(reassembled_len != sizeof(expected)); |
1983 | | FAIL_IF(memcmp(expected, output, reassembled_len)); |
1984 | | SCFree(output); |
1985 | | |
1986 | | /* 1 byte, invalid. */ |
1987 | | reassembled_len = 0; |
1988 | | output = NULL; |
1989 | | FAIL_IF(DNP3ReassembleApplicationLayer(payload, 1, &output, |
1990 | | &reassembled_len)); |
1991 | | FAIL_IF(output != NULL); |
1992 | | FAIL_IF(reassembled_len != 0); |
1993 | | |
1994 | | /* 2 bytes, invalid. */ |
1995 | | reassembled_len = 0; |
1996 | | output = NULL; |
1997 | | FAIL_IF(DNP3ReassembleApplicationLayer(payload, 2, &output, |
1998 | | &reassembled_len)); |
1999 | | FAIL_IF(output != NULL); |
2000 | | FAIL_IF(reassembled_len != 0); |
2001 | | |
2002 | | /* 3 bytes, minimum - but that would only be the transport header |
2003 | | * which isn't included in the output. */ |
2004 | | reassembled_len = 0; |
2005 | | output = NULL; |
2006 | | FAIL_IF(DNP3ReassembleApplicationLayer(payload, 3, &output, |
2007 | | &reassembled_len)); |
2008 | | FAIL_IF(output != NULL); |
2009 | | FAIL_IF(reassembled_len != 0); |
2010 | | |
2011 | | /* 4 bytes is the minimum to get any reassembled data. */ |
2012 | | reassembled_len = 0; |
2013 | | output = NULL; |
2014 | | FAIL_IF(!DNP3ReassembleApplicationLayer(payload, 4, &output, |
2015 | | &reassembled_len)); |
2016 | | FAIL_IF(output == NULL); |
2017 | | FAIL_IF(reassembled_len != 1); |
2018 | | |
2019 | | /* Last block too short (by 1 byte) for data + CRC. */ |
2020 | | uint8_t short_payload1[] = { |
2021 | | |
2022 | | 0xff, 0xc9, 0x05, 0x0c, |
2023 | | 0x01, 0x28, 0x01, 0x00, |
2024 | | 0x00, 0x00, 0x01, 0x01, |
2025 | | 0x01, 0x00, 0x00, 0x00, |
2026 | | 0x72, 0xef, /* CRC. */ |
2027 | | |
2028 | | 0xff, 0xc9, 0x05, 0x0c, |
2029 | | 0x01, 0x28, 0x01, 0x00, |
2030 | | 0x00, 0x00, 0x01, 0x01, |
2031 | | 0x01, 0x00, 0x00, 0x00, |
2032 | | 0x72, 0xef, /* CRC. */ |
2033 | | |
2034 | | 0xff, 0xc9, 0x05, 0x0c, |
2035 | | 0x01, 0x28, 0x01, 0x00, |
2036 | | 0x00, 0x00, 0x01, 0x01, |
2037 | | 0x01, 0x00, 0x00, 0x00, |
2038 | | 0x72, 0xef, /* CRC. */ |
2039 | | |
2040 | | 0x00, 0x00 |
2041 | | }; |
2042 | | reassembled_len = 0; |
2043 | | FAIL_IF(DNP3ReassembleApplicationLayer(short_payload1, |
2044 | | sizeof(short_payload1), &output, &reassembled_len)); |
2045 | | |
2046 | | /* Last block too short (by 2 bytes) for data + CRC. */ |
2047 | | uint8_t short_payload2[] = { |
2048 | | |
2049 | | 0xff, 0xc9, 0x05, 0x0c, |
2050 | | 0x01, 0x28, 0x01, 0x00, |
2051 | | 0x00, 0x00, 0x01, 0x01, |
2052 | | 0x01, 0x00, 0x00, 0x00, |
2053 | | 0x72, 0xef, /* CRC. */ |
2054 | | |
2055 | | 0xff, 0xc9, 0x05, 0x0c, |
2056 | | 0x01, 0x28, 0x01, 0x00, |
2057 | | 0x00, 0x00, 0x01, 0x01, |
2058 | | 0x01, 0x00, 0x00, 0x00, |
2059 | | 0x72, 0xef, /* CRC. */ |
2060 | | |
2061 | | 0xff, 0xc9, 0x05, 0x0c, |
2062 | | 0x01, 0x28, 0x01, 0x00, |
2063 | | 0x00, 0x00, 0x01, 0x01, |
2064 | | 0x01, 0x00, 0x00, 0x00, |
2065 | | 0x72, 0xef, /* CRC. */ |
2066 | | |
2067 | | 0x00, |
2068 | | }; |
2069 | | reassembled_len = 0; |
2070 | | FAIL_IF(DNP3ReassembleApplicationLayer(short_payload2, |
2071 | | sizeof(short_payload2), &output, &reassembled_len)); |
2072 | | |
2073 | | PASS; |
2074 | | } |
2075 | | |
2076 | | /** |
2077 | | * \test Test the probing parser. |
2078 | | */ |
2079 | | static int DNP3ProbingParserTest(void) |
2080 | | { |
2081 | | uint8_t pkt[] = { |
2082 | | 0x05, 0x64, 0x05, 0xc9, 0x03, 0x00, 0x04, 0x00, |
2083 | | 0xbd, 0x71 |
2084 | | }; |
2085 | | uint8_t rdir = 0; |
2086 | | |
2087 | | /* Valid frame. */ |
2088 | | FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_DNP3); |
2089 | | |
2090 | | /* Send too little bytes. */ |
2091 | | FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(DNP3LinkHeader) - 1, &rdir) != ALPROTO_UNKNOWN); |
2092 | | |
2093 | | /* Bad start bytes. */ |
2094 | | pkt[0] = 0x06; |
2095 | | FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_FAILED); |
2096 | | |
2097 | | /* Restore start byte. */ |
2098 | | pkt[0] = 0x05; |
2099 | | |
2100 | | /* Set the length to a value less than the minimum length of 5. */ |
2101 | | pkt[2] = 0x03; |
2102 | | FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, pkt, sizeof(pkt), &rdir) != ALPROTO_FAILED); |
2103 | | |
2104 | | /* Send a banner. */ |
2105 | | char mybanner[] = "Welcome to DNP3 SCADA."; |
2106 | | FAIL_IF(DNP3ProbingParser(NULL, STREAM_TOSERVER, (uint8_t *)mybanner, sizeof(mybanner) - 1, |
2107 | | &rdir) != ALPROTO_DNP3); |
2108 | | FAIL_IF(rdir != STREAM_TOCLIENT); |
2109 | | |
2110 | | PASS; |
2111 | | } |
2112 | | |
2113 | | /** |
2114 | | * \test Test a basic request/response. |
2115 | | */ |
2116 | | static int DNP3ParserTestRequestResponse(void) |
2117 | | { |
2118 | | DNP3State *state = NULL; |
2119 | | |
2120 | | uint8_t request[] = { |
2121 | | /* DNP3 start. */ |
2122 | | 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, |
2123 | | 0xa5, 0xe9, |
2124 | | |
2125 | | /* Transport header. */ |
2126 | | 0xff, |
2127 | | |
2128 | | /* Application layer. */ |
2129 | | 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, |
2130 | | 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, |
2131 | | 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff |
2132 | | }; |
2133 | | |
2134 | | uint8_t response[] = { |
2135 | | /* DNP3 start. */ |
2136 | | 0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00, |
2137 | | 0xe2, 0x59, |
2138 | | |
2139 | | /* Transport header. */ |
2140 | | 0xc3, |
2141 | | |
2142 | | /* Application layer. */ |
2143 | | 0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01, |
2144 | | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x7a, |
2145 | | 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2146 | | 0xff, 0xff |
2147 | | }; |
2148 | | |
2149 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2150 | | Flow flow; |
2151 | | TcpSession ssn; |
2152 | | |
2153 | | memset(&flow, 0, sizeof(flow)); |
2154 | | memset(&ssn, 0, sizeof(ssn)); |
2155 | | |
2156 | | flow.protoctx = (void *)&ssn; |
2157 | | flow.proto = IPPROTO_TCP; |
2158 | | flow.alproto = ALPROTO_DNP3; |
2159 | | |
2160 | | StreamTcpInitConfig(true); |
2161 | | |
2162 | | SCMutexLock(&flow.m); |
2163 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2164 | | STREAM_TOSERVER, request, sizeof(request))); |
2165 | | SCMutexUnlock(&flow.m); |
2166 | | |
2167 | | state = flow.alstate; |
2168 | | FAIL_IF(state == NULL); |
2169 | | |
2170 | | DNP3Transaction *tx = DNP3GetTx(state, 0); |
2171 | | FAIL_IF(tx == NULL); |
2172 | | FAIL_IF(tx->tx_num != 1); |
2173 | | FAIL_IF(tx != state->curr); |
2174 | | FAIL_IF(tx->buffer == NULL); |
2175 | | FAIL_IF(tx->buffer_len != 20); |
2176 | | FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE); |
2177 | | |
2178 | | SCMutexLock(&flow.m); |
2179 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2180 | | STREAM_TOCLIENT, response, sizeof(response))); |
2181 | | SCMutexUnlock(&flow.m); |
2182 | | DNP3Transaction *tx0 = DNP3GetTx(state, 1); |
2183 | | FAIL_IF(tx0 == NULL); |
2184 | | FAIL_IF(tx0 == tx); |
2185 | | FAIL_IF(!tx0->done); |
2186 | | FAIL_IF(tx0->buffer == NULL); |
2187 | | |
2188 | | AppLayerParserThreadCtxFree(alp_tctx); |
2189 | | StreamTcpFreeConfig(true); |
2190 | | FLOW_DESTROY(&flow); |
2191 | | PASS; |
2192 | | } |
2193 | | |
2194 | | /** |
2195 | | * \test Test an unsolicited response from an outstation. |
2196 | | * |
2197 | | * This is kind of like a request initiated from the "server". |
2198 | | */ |
2199 | | static int DNP3ParserTestUnsolicitedResponseConfirm(void) |
2200 | | { |
2201 | | DNP3State *state = NULL; |
2202 | | |
2203 | | /* Unsolicited response with confirm bit set. */ |
2204 | | uint8_t response[] = { |
2205 | | 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, |
2206 | | 0x89, 0xe5, 0xc4, 0xfa, 0x82, 0x00, 0x00, 0x02, |
2207 | | 0x02, 0x17, 0x01, 0x01, 0x81, 0xa7, 0x75, 0xd8, |
2208 | | 0x32, 0x4c, 0x81, 0x3e, 0x01, 0xa1, 0xc9 |
2209 | | }; |
2210 | | |
2211 | | /* Confirm. */ |
2212 | | uint8_t confirm[] = { |
2213 | | 0x05, 0x64, 0x08, 0xc4, 0x02, 0x00, |
2214 | | 0x01, 0x00, 0xd3, 0xb7, 0xc0, 0xda, 0x00, 0x6a, |
2215 | | 0x3d |
2216 | | }; |
2217 | | |
2218 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2219 | | Flow flow; |
2220 | | TcpSession ssn; |
2221 | | |
2222 | | memset(&flow, 0, sizeof(flow)); |
2223 | | memset(&ssn, 0, sizeof(ssn)); |
2224 | | |
2225 | | flow.protoctx = (void *)&ssn; |
2226 | | flow.proto = IPPROTO_TCP; |
2227 | | flow.alproto = ALPROTO_DNP3; |
2228 | | |
2229 | | StreamTcpInitConfig(true); |
2230 | | |
2231 | | SCMutexLock(&flow.m); |
2232 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2233 | | STREAM_TOCLIENT, response, sizeof(response))); |
2234 | | SCMutexUnlock(&flow.m); |
2235 | | |
2236 | | state = flow.alstate; |
2237 | | FAIL_IF(state == NULL); |
2238 | | |
2239 | | DNP3Transaction *tx = DNP3GetTx(state, 0); |
2240 | | FAIL_IF(tx == NULL); |
2241 | | FAIL_IF(tx->tx_num != 1); |
2242 | | FAIL_IF(tx != state->curr); |
2243 | | FAIL_IF(!tx->done); |
2244 | | FAIL_IF(tx->ah.function_code != DNP3_APP_FC_UNSOLICITED_RESP); |
2245 | | |
2246 | | SCMutexLock(&flow.m); |
2247 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2248 | | STREAM_TOSERVER, confirm, sizeof(confirm))); |
2249 | | SCMutexUnlock(&flow.m); |
2250 | | |
2251 | | /* Confirms are ignored currently. With the move to |
2252 | | unidirectional transactions it might be easy to support these |
2253 | | now. */ |
2254 | | DNP3Transaction *resptx = DNP3GetTx(state, 1); |
2255 | | FAIL_IF(resptx); |
2256 | | |
2257 | | AppLayerParserThreadCtxFree(alp_tctx); |
2258 | | StreamTcpFreeConfig(true); |
2259 | | FLOW_DESTROY(&flow); |
2260 | | PASS; |
2261 | | } |
2262 | | |
2263 | | /** |
2264 | | * \test Test flood state. |
2265 | | * |
2266 | | * Note that flood state needs to revisited with the modification to a |
2267 | | * unidirectional protocol. |
2268 | | */ |
2269 | | static int DNP3ParserTestFlooded(void) |
2270 | | { |
2271 | | DNP3State *state = NULL; |
2272 | | |
2273 | | uint8_t request[] = { |
2274 | | /* DNP3 start. */ |
2275 | | 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, |
2276 | | 0xa5, 0xe9, |
2277 | | |
2278 | | /* Transport header. */ |
2279 | | 0xff, |
2280 | | |
2281 | | /* Application layer. */ |
2282 | | 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, |
2283 | | 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, |
2284 | | 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff |
2285 | | }; |
2286 | | |
2287 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2288 | | Flow flow; |
2289 | | TcpSession ssn; |
2290 | | |
2291 | | memset(&flow, 0, sizeof(flow)); |
2292 | | memset(&ssn, 0, sizeof(ssn)); |
2293 | | |
2294 | | flow.protoctx = (void *)&ssn; |
2295 | | flow.proto = IPPROTO_TCP; |
2296 | | flow.alproto = ALPROTO_DNP3; |
2297 | | |
2298 | | StreamTcpInitConfig(true); |
2299 | | |
2300 | | SCMutexLock(&flow.m); |
2301 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2302 | | STREAM_TOSERVER, request, sizeof(request))); |
2303 | | SCMutexUnlock(&flow.m); |
2304 | | |
2305 | | state = flow.alstate; |
2306 | | FAIL_IF(state == NULL); |
2307 | | |
2308 | | DNP3Transaction *tx = DNP3GetTx(state, 0); |
2309 | | FAIL_IF(tx == NULL); |
2310 | | FAIL_IF(tx->tx_num != 1); |
2311 | | FAIL_IF(tx != state->curr); |
2312 | | FAIL_IF(tx->buffer == NULL); |
2313 | | FAIL_IF(tx->buffer_len != 20); |
2314 | | FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE); |
2315 | | FAIL_IF_NOT(tx->done); |
2316 | | FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER)); |
2317 | | |
2318 | | for (uint64_t i = 0; i < dnp3_max_tx - 1; i++) { |
2319 | | SCMutexLock(&flow.m); |
2320 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2321 | | STREAM_TOSERVER, request, sizeof(request))); |
2322 | | SCMutexUnlock(&flow.m); |
2323 | | } |
2324 | | FAIL_IF(state->flooded); |
2325 | | FAIL_IF_NOT(DNP3GetAlstateProgress(tx, STREAM_TOSERVER)); |
2326 | | |
2327 | | /* One more request should trip us into flooded state. */ |
2328 | | SCMutexLock(&flow.m); |
2329 | | FAIL_IF(AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2330 | | STREAM_TOSERVER, request, sizeof(request))); |
2331 | | SCMutexUnlock(&flow.m); |
2332 | | FAIL_IF(!state->flooded); |
2333 | | |
2334 | | /* Progress for the oldest tx should return 1. */ |
2335 | | FAIL_IF(!DNP3GetAlstateProgress(tx, 0)); |
2336 | | |
2337 | | AppLayerParserThreadCtxFree(alp_tctx); |
2338 | | StreamTcpFreeConfig(true); |
2339 | | FLOW_DESTROY(&flow); |
2340 | | PASS; |
2341 | | } |
2342 | | |
2343 | | /** |
2344 | | * \test Test parsing of partial frames. |
2345 | | * |
2346 | | * As DNP3 operates over TCP, it is possible that a partial DNP3 frame |
2347 | | * is received. Test that the partial frame will be buffered until the |
2348 | | * remainder is seen. |
2349 | | */ |
2350 | | static int DNP3ParserTestPartialFrame(void) |
2351 | | { |
2352 | | DNP3State *state = NULL; |
2353 | | DNP3Transaction *tx; |
2354 | | int r; |
2355 | | |
2356 | | uint8_t request_partial1[] = { |
2357 | | /* DNP3 start. */ |
2358 | | 0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00, |
2359 | | 0xa5, 0xe9, |
2360 | | |
2361 | | /* Transport header. */ |
2362 | | 0xff, |
2363 | | |
2364 | | /* Application layer. */ |
2365 | | 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00, |
2366 | | }; |
2367 | | |
2368 | | uint8_t request_partial2[] = { |
2369 | | /* Remainder of application layer. */ |
2370 | | 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x72, |
2371 | | 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff |
2372 | | }; |
2373 | | |
2374 | | uint8_t response_partial1[] = { |
2375 | | /* DNP3 start. */ |
2376 | | 0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00, |
2377 | | 0xe2, 0x59, |
2378 | | |
2379 | | /* Transport header. */ |
2380 | | 0xc3, |
2381 | | |
2382 | | /* Application layer. */ |
2383 | | 0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01, |
2384 | | }; |
2385 | | |
2386 | | uint8_t response_partial2[] = { |
2387 | | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x7a, |
2388 | | 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2389 | | 0xff, 0xff |
2390 | | }; |
2391 | | |
2392 | | /* Boiler plate for app layer setup. */ |
2393 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2394 | | Flow flow; |
2395 | | TcpSession ssn; |
2396 | | memset(&flow, 0, sizeof(flow)); |
2397 | | memset(&ssn, 0, sizeof(ssn)); |
2398 | | flow.protoctx = (void *)&ssn; |
2399 | | flow.proto = IPPROTO_TCP; |
2400 | | flow.alproto = ALPROTO_DNP3; |
2401 | | StreamTcpInitConfig(true); |
2402 | | |
2403 | | /* Pass in the first partial frame. */ |
2404 | | |
2405 | | SCMutexLock(&flow.m); |
2406 | | r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2407 | | STREAM_TOSERVER, request_partial1, sizeof(request_partial1)); |
2408 | | SCMutexUnlock(&flow.m); |
2409 | | FAIL_IF(r != 0); |
2410 | | |
2411 | | /* Frame should just be buffered, but not yet processed. */ |
2412 | | state = flow.alstate; |
2413 | | FAIL_IF(state == NULL); |
2414 | | FAIL_IF(state->request_buffer.len != sizeof(request_partial1)); |
2415 | | FAIL_IF(state->request_buffer.offset != 0); |
2416 | | FAIL_IF(memcmp(state->request_buffer.buffer, request_partial1, |
2417 | | sizeof(request_partial1))); |
2418 | | |
2419 | | /* There should not be a transaction yet. */ |
2420 | | FAIL_IF(state->transaction_max != 0); |
2421 | | FAIL_IF(DNP3GetTx(state, 0) != NULL); |
2422 | | |
2423 | | /* Send the second partial. */ |
2424 | | SCMutexLock(&flow.m); |
2425 | | r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2426 | | STREAM_TOSERVER, request_partial2, sizeof(request_partial2)); |
2427 | | SCMutexUnlock(&flow.m); |
2428 | | FAIL_IF(r != 0); |
2429 | | |
2430 | | /* The second partial completed the frame, the buffer should now |
2431 | | * be clear. */ |
2432 | | FAIL_IF(state->request_buffer.len != 0); |
2433 | | FAIL_IF(state->request_buffer.offset != 0); |
2434 | | |
2435 | | /* Should now have a complete transaction. */ |
2436 | | tx = DNP3GetTx(state, 0); |
2437 | | FAIL_IF(tx == NULL); |
2438 | | FAIL_IF(tx->tx_num != 1); |
2439 | | FAIL_IF(tx != state->curr); |
2440 | | FAIL_IF(tx->buffer == NULL); |
2441 | | FAIL_IF(tx->buffer_len != 20); |
2442 | | FAIL_IF(tx->ah.function_code != DNP3_APP_FC_DIR_OPERATE); |
2443 | | |
2444 | | /* Send partial response. */ |
2445 | | SCMutexLock(&flow.m); |
2446 | | r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2447 | | STREAM_TOCLIENT, response_partial1, sizeof(response_partial1)); |
2448 | | SCMutexUnlock(&flow.m); |
2449 | | FAIL_IF(r != 0); |
2450 | | FAIL_IF(state->response_buffer.len != sizeof(response_partial1)); |
2451 | | FAIL_IF(state->response_buffer.offset != 0); |
2452 | | tx = DNP3GetTx(state, 1); |
2453 | | FAIL_IF_NOT_NULL(tx); |
2454 | | |
2455 | | /* Send rest of response. */ |
2456 | | SCMutexLock(&flow.m); |
2457 | | r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2458 | | STREAM_TOCLIENT, response_partial2, sizeof(response_partial2)); |
2459 | | SCMutexUnlock(&flow.m); |
2460 | | FAIL_IF(r != 0); |
2461 | | |
2462 | | /* Buffer should now be empty. */ |
2463 | | FAIL_IF(state->response_buffer.len != 0); |
2464 | | FAIL_IF(state->response_buffer.offset != 0); |
2465 | | |
2466 | | /* There should now be a response transaction. */ |
2467 | | tx = DNP3GetTx(state, 1); |
2468 | | FAIL_IF_NULL(tx); |
2469 | | FAIL_IF(tx->buffer == NULL); |
2470 | | FAIL_IF(tx->buffer_len == 0); |
2471 | | |
2472 | | AppLayerParserThreadCtxFree(alp_tctx); |
2473 | | StreamTcpFreeConfig(true); |
2474 | | FLOW_DESTROY(&flow); |
2475 | | PASS; |
2476 | | } |
2477 | | |
2478 | | /** |
2479 | | * \test Test multiple DNP3 frames in one TCP read. |
2480 | | */ |
2481 | | static int DNP3ParserTestMultiFrame(void) |
2482 | | { |
2483 | | DNP3State *state = NULL; |
2484 | | |
2485 | | /* Unsolicited response 1. */ |
2486 | | uint8_t unsol_response1[] = { |
2487 | | 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, |
2488 | | 0x89, 0xe5, 0xc4, 0xfa, 0x82, 0x00, 0x00, 0x02, |
2489 | | 0x02, 0x17, 0x01, 0x01, 0x81, 0xa7, 0x75, 0xd8, |
2490 | | 0x32, 0x4c, 0x81, 0x3e, 0x01, 0xa1, 0xc9, |
2491 | | }; |
2492 | | |
2493 | | /* Unsolicited response 2. */ |
2494 | | uint8_t unsol_response2[] = { |
2495 | | 0x05, 0x64, 0x16, 0x44, 0x01, 0x00, 0x02, 0x00, |
2496 | | 0x89, 0xe5, 0xc5, 0xfb, 0x82, 0x00, 0x00, 0x02, |
2497 | | 0x02, 0x17, 0x01, 0x0c, 0x01, 0xd8, 0x75, 0xd8, |
2498 | | 0x32, 0x4c, 0xc9, 0x3c, 0x01, 0xa1, 0xc9, |
2499 | | }; |
2500 | | |
2501 | | uint8_t combined[sizeof(unsol_response1) + sizeof(unsol_response2)]; |
2502 | | memcpy(combined, unsol_response1, sizeof(unsol_response1)); |
2503 | | memcpy(combined + sizeof(unsol_response1), unsol_response2, |
2504 | | sizeof(unsol_response2)); |
2505 | | |
2506 | | /* Setup. */ |
2507 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2508 | | Flow flow; |
2509 | | TcpSession ssn; |
2510 | | int r; |
2511 | | memset(&flow, 0, sizeof(flow)); |
2512 | | memset(&ssn, 0, sizeof(ssn)); |
2513 | | flow.protoctx = (void *)&ssn; |
2514 | | flow.proto = IPPROTO_TCP; |
2515 | | flow.alproto = ALPROTO_DNP3; |
2516 | | StreamTcpInitConfig(true); |
2517 | | |
2518 | | SCMutexLock(&flow.m); |
2519 | | r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2520 | | STREAM_TOCLIENT, combined, sizeof(combined)); |
2521 | | SCMutexUnlock(&flow.m); |
2522 | | FAIL_IF(r != 0); |
2523 | | |
2524 | | state = flow.alstate; |
2525 | | FAIL_IF(state == NULL); |
2526 | | FAIL_IF(state->transaction_max != 2); |
2527 | | |
2528 | | AppLayerParserThreadCtxFree(alp_tctx); |
2529 | | StreamTcpFreeConfig(true); |
2530 | | FLOW_DESTROY(&flow); |
2531 | | PASS; |
2532 | | } |
2533 | | |
2534 | | /** |
2535 | | * \test Test the parsing of a request PDU. |
2536 | | * |
2537 | | * The PDU under test contains a single read request object: |
2538 | | * - Group: 1 |
2539 | | * - Variation: 0 |
2540 | | * - Count: 0 |
2541 | | */ |
2542 | | static int DNP3ParserTestParsePDU01(void) |
2543 | | { |
2544 | | /* Frame to be tested. This frame is a DNP3 request with one read |
2545 | | * request data object, group 1, variation 0. */ |
2546 | | const uint8_t pkt[] = { |
2547 | | 0x05, 0x64, |
2548 | | 0x0b, 0xc4, 0x17, 0x00, 0xef, 0xff, 0xc4, 0x8f, |
2549 | | 0xe1, 0xc8, 0x01, 0x01, 0x00, 0x06, 0x77, 0x6e |
2550 | | }; |
2551 | | |
2552 | | DNP3State *dnp3state = DNP3StateAlloc(NULL, ALPROTO_UNKNOWN); |
2553 | | int pdus = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); |
2554 | | FAIL_IF(pdus < 1); |
2555 | | DNP3Transaction *dnp3tx = DNP3GetTx(dnp3state, 0); |
2556 | | FAIL_IF_NULL(dnp3tx); |
2557 | | FAIL_IF(!dnp3tx->is_request); |
2558 | | FAIL_IF(TAILQ_EMPTY(&dnp3tx->objects)); |
2559 | | DNP3Object *object = TAILQ_FIRST(&dnp3tx->objects); |
2560 | | FAIL_IF(object->group != 1 || object->variation != 0); |
2561 | | FAIL_IF(object->count != 0); |
2562 | | |
2563 | | DNP3StateFree(dnp3state); |
2564 | | PASS; |
2565 | | } |
2566 | | |
2567 | | /** |
2568 | | * \test Test the decode of a DNP3 fragment with a single 70:3 object. |
2569 | | */ |
2570 | | static int DNP3ParserDecodeG70V3Test(void) |
2571 | | { |
2572 | | const uint8_t pkt[] = { |
2573 | | 0x05, 0x64, |
2574 | | 0x63, 0xc4, 0x04, 0x00, 0x03, 0x00, 0xc7, 0xee, |
2575 | | 0xc7, 0xc9, 0x1b, 0x46, 0x03, 0x5b, 0x01, 0x55, |
2576 | | 0x00, 0x1a, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, |
2577 | | 0x9e, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2578 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2579 | | 0x00, 0x00, 0xff, 0xff, 0x00, 0x1e, 0x00, 0x43, |
2580 | | 0x3a, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x2f, 0x44, |
2581 | | 0x4e, 0x50, 0x44, 0x65, 0x67, 0x7d, 0x76, 0x69, |
2582 | | 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, |
2583 | | 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x93, 0x0c, |
2584 | | 0x6e, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, |
2585 | | 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x52, 0x65, 0x6d, |
2586 | | 0x35, 0x20, 0x6f, 0x74, 0x65, 0x20, 0x44, 0x65, |
2587 | | 0x76, 0x69, 0x63, 0x65, 0x2e, 0x78, 0x6d, 0x6c, |
2588 | | 0xc4, 0x8b |
2589 | | }; |
2590 | | |
2591 | | DNP3State *dnp3state = DNP3StateAlloc(NULL, ALPROTO_UNKNOWN); |
2592 | | FAIL_IF_NULL(dnp3state); |
2593 | | int bytes = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); |
2594 | | FAIL_IF(bytes != sizeof(pkt)); |
2595 | | DNP3Transaction *tx = DNP3GetTx(dnp3state, 0); |
2596 | | FAIL_IF_NULL(tx); |
2597 | | FAIL_IF_NOT(tx->is_request); |
2598 | | DNP3Object *obj = TAILQ_FIRST(&tx->objects); |
2599 | | FAIL_IF_NULL(obj); |
2600 | | FAIL_IF_NOT(obj->group == 70); |
2601 | | FAIL_IF_NOT(obj->variation == 3); |
2602 | | FAIL_IF_NOT(obj->prefix_code == 0x5); |
2603 | | FAIL_IF_NOT(obj->range_code == 0xb); |
2604 | | FAIL_IF_NOT(obj->count == 1); |
2605 | | DNP3Point *point = TAILQ_FIRST(obj->points); |
2606 | | FAIL_IF_NULL(point); |
2607 | | FAIL_IF_NOT(point->prefix == 85); |
2608 | | FAIL_IF_NOT(point->size == 85); |
2609 | | FAIL_IF_NULL(point->data); |
2610 | | DNP3ObjectG70V3 *data = point->data; |
2611 | | FAIL_IF_NOT(strcmp( |
2612 | | data->filename, |
2613 | | "C:/temp/DNPDeviceConfiguration written to Remote Device.xml") == 0); |
2614 | | DNP3StateFree(dnp3state); |
2615 | | PASS; |
2616 | | } |
2617 | | |
2618 | | /** |
2619 | | * \brief Test that an alert is raised on an unknown object. |
2620 | | */ |
2621 | | static int DNP3ParserUnknownEventAlertTest(void) |
2622 | | { |
2623 | | /* Valid DNP3 frame with 70:3 object. */ |
2624 | | uint8_t pkt[] = { |
2625 | | 0x05, 0x64, 0x63, 0xc4, 0x04, 0x00, 0x03, 0x00, |
2626 | | 0xc7, 0xee, |
2627 | | |
2628 | | 0xc7, 0xc9, 0x1b, |
2629 | | |
2630 | | /* Object and variation. Originally 70:3, now 70:99, an |
2631 | | * unknown object. */ |
2632 | | 0x46, 0x63, |
2633 | | |
2634 | | 0x5b, 0x01, 0x55, |
2635 | | 0x00, 0x1a, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, |
2636 | | 0x9e, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2637 | | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
2638 | | 0x00, 0x00, 0xff, 0xff, 0x00, 0x1e, 0x00, 0x43, |
2639 | | 0x3a, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x2f, 0x44, |
2640 | | 0x4e, 0x50, 0x44, 0x65, 0x67, 0x7d, 0x76, 0x69, |
2641 | | 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, |
2642 | | 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x93, 0x0c, |
2643 | | 0x6e, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, |
2644 | | 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x52, 0x65, 0x6d, |
2645 | | 0x35, 0x20, 0x6f, 0x74, 0x65, 0x20, 0x44, 0x65, |
2646 | | 0x76, 0x69, 0x63, 0x65, 0x2e, 0x78, 0x6d, 0x6c, |
2647 | | 0xc4, 0x8b |
2648 | | }; |
2649 | | |
2650 | | DNP3FixCrc(pkt + 10, sizeof(pkt) - 10); |
2651 | | |
2652 | | DNP3State *dnp3state = DNP3StateAlloc(NULL, ALPROTO_UNKNOWN); |
2653 | | FAIL_IF_NULL(dnp3state); |
2654 | | int bytes = DNP3HandleRequestLinkLayer(dnp3state, pkt, sizeof(pkt)); |
2655 | | FAIL_IF(bytes != sizeof(pkt)); |
2656 | | |
2657 | | DNP3StateFree(dnp3state); |
2658 | | PASS; |
2659 | | } |
2660 | | |
2661 | | /** |
2662 | | * \brief Test that an alert is raised on incorrect data. |
2663 | | */ |
2664 | | static int DNP3ParserIncorrectUserData(void) |
2665 | | { |
2666 | | uint8_t packet_bytes[] = { |
2667 | | 0x05, 0x64, 0x08, 0xc4, 0x03, 0x00, 0x04, 0x00, |
2668 | | 0xbf, 0xe9, 0xc1, 0xc1, 0x82, 0xc5, 0xee |
2669 | | }; |
2670 | | |
2671 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
2672 | | Flow flow; |
2673 | | TcpSession ssn; |
2674 | | memset(&flow, 0, sizeof(flow)); |
2675 | | memset(&ssn, 0, sizeof(ssn)); |
2676 | | flow.protoctx = (void *)&ssn; |
2677 | | flow.proto = IPPROTO_TCP; |
2678 | | flow.alproto = ALPROTO_DNP3; |
2679 | | StreamTcpInitConfig(true); |
2680 | | |
2681 | | int r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3, |
2682 | | STREAM_TOCLIENT, packet_bytes, sizeof(packet_bytes)); |
2683 | | |
2684 | | FAIL_IF(r == 0); |
2685 | | |
2686 | | AppLayerParserThreadCtxFree(alp_tctx); |
2687 | | StreamTcpFreeConfig(true); |
2688 | | FLOW_DESTROY(&flow); |
2689 | | PASS; |
2690 | | } |
2691 | | |
2692 | | #endif |
2693 | | |
2694 | | void DNP3ParserRegisterTests(void) |
2695 | 0 | { |
2696 | | #ifdef UNITTESTS |
2697 | | UtRegisterTest("DNP3ParserTestCheckCRC", DNP3ParserTestCheckCRC); |
2698 | | UtRegisterTest("DNP3ParserCheckLinkHeaderCRC", |
2699 | | DNP3ParserCheckLinkHeaderCRC); |
2700 | | UtRegisterTest("DNP3CheckUserDataCRCsTest", DNP3CheckUserDataCRCsTest); |
2701 | | UtRegisterTest("DNP3CalculateLinkLengthTest", DNP3CalculateLinkLengthTest); |
2702 | | UtRegisterTest("DNP3CalculateTransportLengthWithoutCRCsTest", |
2703 | | DNP3CalculateTransportLengthWithoutCRCsTest); |
2704 | | UtRegisterTest("DNP3ReassembleApplicationLayerTest01", |
2705 | | DNP3ReassembleApplicationLayerTest01); |
2706 | | UtRegisterTest("DNP3ProbingParserTest", DNP3ProbingParserTest); |
2707 | | UtRegisterTest("DNP3ParserTestRequestResponse", |
2708 | | DNP3ParserTestRequestResponse); |
2709 | | UtRegisterTest("DNP3ParserTestUnsolicitedResponseConfirm", |
2710 | | DNP3ParserTestUnsolicitedResponseConfirm); |
2711 | | UtRegisterTest("DNP3ParserTestPartialFrame", DNP3ParserTestPartialFrame); |
2712 | | UtRegisterTest("DNP3ParserTestMultiFrame", DNP3ParserTestMultiFrame); |
2713 | | UtRegisterTest("DNP3ParserTestFlooded", DNP3ParserTestFlooded); |
2714 | | UtRegisterTest("DNP3ParserTestParsePDU01", DNP3ParserTestParsePDU01); |
2715 | | UtRegisterTest("DNP3ParserDecodeG70V3Test", DNP3ParserDecodeG70V3Test); |
2716 | | UtRegisterTest("DNP3ParserUnknownEventAlertTest", |
2717 | | DNP3ParserUnknownEventAlertTest); |
2718 | | UtRegisterTest("DNP3ParserIncorrectUserData", DNP3ParserIncorrectUserData); |
2719 | | #endif |
2720 | 0 | } |