/src/suricata7/src/app-layer-enip.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 | | * \author Kevin Wong <kwong@solananetworks.com> |
22 | | * |
23 | | * App-layer parser for ENIP protocol |
24 | | * |
25 | | */ |
26 | | |
27 | | #include "suricata-common.h" |
28 | | #include "suricata.h" |
29 | | |
30 | | #include "util-debug.h" |
31 | | #include "util-byte.h" |
32 | | #include "util-enum.h" |
33 | | #include "util-mem.h" |
34 | | #include "util-misc.h" |
35 | | |
36 | | #include "stream.h" |
37 | | |
38 | | #include "app-layer.h" |
39 | | #include "app-layer-protos.h" |
40 | | #include "app-layer-parser.h" |
41 | | #include "app-layer-enip.h" |
42 | | #include "app-layer-enip-common.h" |
43 | | |
44 | | #include "app-layer-detect-proto.h" |
45 | | |
46 | | #include "conf.h" |
47 | | #include "decode.h" |
48 | | |
49 | | #include "detect-parse.h" |
50 | | #include "detect-engine.h" |
51 | | #include "util-unittest.h" |
52 | | #include "util-unittest-helper.h" |
53 | | #include "pkt-var.h" |
54 | | #include "util-profiling.h" |
55 | | |
56 | | |
57 | | SCEnumCharMap enip_decoder_event_table[ ] = { |
58 | | { NULL, -1 }, |
59 | | }; |
60 | | |
61 | | /** \brief get value for 'complete' status in ENIP |
62 | | * |
63 | | * For ENIP we use a simple bool. |
64 | | */ |
65 | | static int ENIPGetAlstateProgress(void *tx, uint8_t direction) |
66 | 1.30M | { |
67 | 1.30M | return 1; |
68 | 1.30M | } |
69 | | |
70 | | static AppLayerTxData *ENIPGetTxData(void *vtx) |
71 | 682k | { |
72 | 682k | ENIPTransaction *tx = (ENIPTransaction *)vtx; |
73 | 682k | return &tx->tx_data; |
74 | 682k | } |
75 | | |
76 | | static AppLayerStateData *ENIPGetStateData(void *vstate) |
77 | 356 | { |
78 | 356 | ENIPState *state = (ENIPState *)vstate; |
79 | 356 | return &state->state_data; |
80 | 356 | } |
81 | | |
82 | | static void *ENIPGetTx(void *alstate, uint64_t tx_id) |
83 | 2.20k | { |
84 | 2.20k | ENIPState *enip = (ENIPState *) alstate; |
85 | 2.20k | ENIPTransaction *tx = NULL; |
86 | | |
87 | 2.20k | if (enip->curr && enip->curr->tx_num == tx_id + 1) |
88 | 75 | return enip->curr; |
89 | | |
90 | 3.38k | TAILQ_FOREACH(tx, &enip->tx_list, next) { |
91 | 3.38k | if (tx->tx_num != (tx_id+1)) |
92 | 1.25k | continue; |
93 | | |
94 | 2.12k | SCLogDebug("returning tx %p", tx); |
95 | 2.12k | return tx; |
96 | 3.38k | } |
97 | | |
98 | 0 | return NULL; |
99 | 2.12k | } |
100 | | |
101 | | static uint64_t ENIPGetTxCnt(void *alstate) |
102 | 1.64M | { |
103 | 1.64M | return ((ENIPState *)alstate)->transaction_max; |
104 | 1.64M | } |
105 | | |
106 | | static int ENIPStateGetEventInfo(const char *event_name, int *event_id, AppLayerEventType *event_type) |
107 | 1 | { |
108 | 1 | *event_id = SCMapEnumNameToValue(event_name, enip_decoder_event_table); |
109 | | |
110 | 1 | if (*event_id == -1) { |
111 | 1 | SCLogError("event \"%s\" not present in " |
112 | 1 | "enip's enum map table.", |
113 | 1 | event_name); |
114 | | /* yes this is fatal */ |
115 | 1 | return -1; |
116 | 1 | } |
117 | | |
118 | 0 | *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; |
119 | |
|
120 | 0 | return 0; |
121 | 1 | } |
122 | | |
123 | | static int ENIPStateGetEventInfoById(int event_id, const char **event_name, |
124 | | AppLayerEventType *event_type) |
125 | 0 | { |
126 | 0 | *event_name = SCMapEnumValueToName(event_id, enip_decoder_event_table); |
127 | 0 | if (*event_name == NULL) { |
128 | 0 | SCLogError("event \"%d\" not present in " |
129 | 0 | "enip's enum map table.", |
130 | 0 | event_id); |
131 | | /* yes this is fatal */ |
132 | 0 | return -1; |
133 | 0 | } |
134 | | |
135 | 0 | *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION; |
136 | |
|
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | | /** \brief Allocate enip state |
141 | | * |
142 | | * return state |
143 | | */ |
144 | | static void *ENIPStateAlloc(void *orig_state, AppProto proto_orig) |
145 | 2.51k | { |
146 | 2.51k | SCLogDebug("ENIPStateAlloc"); |
147 | 2.51k | void *s = SCMalloc(sizeof(ENIPState)); |
148 | 2.51k | if (unlikely(s == NULL)) |
149 | 0 | return NULL; |
150 | | |
151 | 2.51k | memset(s, 0, sizeof(ENIPState)); |
152 | | |
153 | 2.51k | ENIPState *enip_state = (ENIPState *) s; |
154 | | |
155 | 2.51k | TAILQ_INIT(&enip_state->tx_list); |
156 | 2.51k | return s; |
157 | 2.51k | } |
158 | | |
159 | | /** \internal |
160 | | * \brief Free a ENIP TX |
161 | | * \param tx ENIP TX to free */ |
162 | | static void ENIPTransactionFree(ENIPTransaction *tx, ENIPState *state) |
163 | 891k | { |
164 | 891k | SCEnter(); |
165 | 891k | SCLogDebug("ENIPTransactionFree"); |
166 | 891k | CIPServiceEntry *svc = NULL; |
167 | 981k | while ((svc = TAILQ_FIRST(&tx->service_list))) |
168 | 90.1k | { |
169 | 90.1k | TAILQ_REMOVE(&tx->service_list, svc, next); |
170 | | |
171 | 90.1k | SegmentEntry *seg = NULL; |
172 | 184k | while ((seg = TAILQ_FIRST(&svc->segment_list))) |
173 | 94.3k | { |
174 | 94.3k | TAILQ_REMOVE(&svc->segment_list, seg, next); |
175 | 94.3k | SCFree(seg); |
176 | 94.3k | } |
177 | | |
178 | 90.1k | AttributeEntry *attr = NULL; |
179 | 4.21M | while ((attr = TAILQ_FIRST(&svc->attrib_list))) |
180 | 4.12M | { |
181 | 4.12M | TAILQ_REMOVE(&svc->attrib_list, attr, next); |
182 | 4.12M | SCFree(attr); |
183 | 4.12M | } |
184 | | |
185 | 90.1k | SCFree(svc); |
186 | 90.1k | } |
187 | | |
188 | 891k | AppLayerDecoderEventsFreeEvents(&tx->tx_data.events); |
189 | | |
190 | 891k | if (tx->tx_data.de_state != NULL) { |
191 | 3.03k | DetectEngineStateFree(tx->tx_data.de_state); |
192 | | |
193 | 3.03k | state->tx_with_detect_state_cnt--; |
194 | 3.03k | } |
195 | | |
196 | 891k | if (state->iter == tx) |
197 | 0 | state->iter = NULL; |
198 | | |
199 | 891k | SCFree(tx); |
200 | 891k | SCReturn; |
201 | 891k | } |
202 | | |
203 | | /** \brief Free enip state |
204 | | * |
205 | | */ |
206 | | static void ENIPStateFree(void *s) |
207 | 2.51k | { |
208 | 2.51k | SCEnter(); |
209 | 2.51k | SCLogDebug("ENIPStateFree"); |
210 | 2.51k | if (s) |
211 | 2.51k | { |
212 | 2.51k | ENIPState *enip_state = (ENIPState *) s; |
213 | | |
214 | 2.51k | ENIPTransaction *tx = NULL; |
215 | 271k | while ((tx = TAILQ_FIRST(&enip_state->tx_list))) |
216 | 268k | { |
217 | 268k | TAILQ_REMOVE(&enip_state->tx_list, tx, next); |
218 | 268k | ENIPTransactionFree(tx, enip_state); |
219 | 268k | } |
220 | | |
221 | 2.51k | if (enip_state->buffer != NULL) |
222 | 0 | { |
223 | 0 | SCFree(enip_state->buffer); |
224 | 0 | } |
225 | | |
226 | 2.51k | SCFree(s); |
227 | 2.51k | } |
228 | 2.51k | SCReturn; |
229 | 2.51k | } |
230 | | |
231 | | /** \internal |
232 | | * \brief Allocate a ENIP TX |
233 | | * \retval tx or NULL */ |
234 | | static ENIPTransaction *ENIPTransactionAlloc(ENIPState *state) |
235 | 891k | { |
236 | 891k | SCLogDebug("ENIPStateTransactionAlloc"); |
237 | 891k | ENIPTransaction *tx = (ENIPTransaction *) SCCalloc(1, |
238 | 891k | sizeof(ENIPTransaction)); |
239 | 891k | if (unlikely(tx == NULL)) |
240 | 0 | return NULL; |
241 | | |
242 | 891k | state->curr = tx; |
243 | 891k | state->transaction_max++; |
244 | | |
245 | 891k | memset(tx, 0x00, sizeof(ENIPTransaction)); |
246 | 891k | TAILQ_INIT(&tx->service_list); |
247 | | |
248 | 891k | tx->enip = state; |
249 | 891k | tx->tx_num = state->transaction_max; |
250 | 891k | tx->service_count = 0; |
251 | | |
252 | 891k | TAILQ_INSERT_TAIL(&state->tx_list, tx, next); |
253 | | |
254 | 891k | return tx; |
255 | 891k | } |
256 | | |
257 | | /** |
258 | | * \brief enip transaction cleanup callback |
259 | | */ |
260 | | static void ENIPStateTransactionFree(void *state, uint64_t tx_id) |
261 | 623k | { |
262 | 623k | SCEnter(); |
263 | 623k | SCLogDebug("ENIPStateTransactionFree"); |
264 | 623k | ENIPState *enip_state = state; |
265 | 623k | ENIPTransaction *tx = NULL; |
266 | 623k | TAILQ_FOREACH(tx, &enip_state->tx_list, next) |
267 | 623k | { |
268 | | |
269 | 623k | if ((tx_id+1) < tx->tx_num) |
270 | 0 | break; |
271 | 623k | else if ((tx_id+1) > tx->tx_num) |
272 | 3 | continue; |
273 | | |
274 | 623k | if (tx == enip_state->curr) |
275 | 530k | enip_state->curr = NULL; |
276 | | |
277 | 623k | if (tx->tx_data.events != NULL) { |
278 | 0 | if (tx->tx_data.events->cnt <= enip_state->events) |
279 | 0 | enip_state->events -= tx->tx_data.events->cnt; |
280 | 0 | else |
281 | 0 | enip_state->events = 0; |
282 | 0 | } |
283 | | |
284 | 623k | TAILQ_REMOVE(&enip_state->tx_list, tx, next); |
285 | 623k | ENIPTransactionFree(tx, state); |
286 | 623k | break; |
287 | 623k | } |
288 | 623k | SCReturn; |
289 | 623k | } |
290 | | |
291 | | /** \internal |
292 | | * |
293 | | * \brief This function is called to retrieve a ENIP |
294 | | * |
295 | | * \param state ENIP state structure for the parser |
296 | | * \param input Input line of the command |
297 | | * \param input_len Length of the request |
298 | | * |
299 | | * \retval 1 when the command is parsed, 0 otherwise |
300 | | */ |
301 | | static AppLayerResult ENIPParse(Flow *f, void *state, AppLayerParserState *pstate, |
302 | | StreamSlice stream_slice, void *local_data, uint8_t direction) |
303 | 542k | { |
304 | 542k | SCEnter(); |
305 | 542k | ENIPState *enip = (ENIPState *) state; |
306 | 542k | ENIPTransaction *tx; |
307 | | |
308 | 542k | const uint8_t *input = StreamSliceGetData(&stream_slice); |
309 | 542k | uint32_t input_len = StreamSliceGetDataLen(&stream_slice); |
310 | | |
311 | 542k | if (input == NULL && AppLayerParserStateIssetFlag(pstate, |
312 | 108 | APP_LAYER_PARSER_EOF_TS|APP_LAYER_PARSER_EOF_TC)) |
313 | 8 | { |
314 | 8 | SCReturnStruct(APP_LAYER_OK); |
315 | 542k | } else if (input == NULL && input_len != 0) { |
316 | | // GAP |
317 | 100 | SCReturnStruct(APP_LAYER_OK); |
318 | 541k | } else if (input == NULL || input_len == 0) |
319 | 0 | { |
320 | 0 | SCReturnStruct(APP_LAYER_ERROR); |
321 | 0 | } |
322 | | |
323 | 891k | while (input_len > 0) |
324 | 891k | { |
325 | 891k | tx = ENIPTransactionAlloc(enip); |
326 | 891k | if (tx == NULL) |
327 | 0 | SCReturnStruct(APP_LAYER_OK); |
328 | | |
329 | 891k | if (direction == STREAM_TOCLIENT) |
330 | 290k | tx->tx_data.detect_flags_ts |= APP_LAYER_TX_SKIP_INSPECT_FLAG; |
331 | 601k | else |
332 | 601k | tx->tx_data.detect_flags_tc |= APP_LAYER_TX_SKIP_INSPECT_FLAG; |
333 | | |
334 | 891k | SCLogDebug("ENIPParse input len %d", input_len); |
335 | 891k | DecodeENIPPDU(input, input_len, tx); |
336 | 891k | uint32_t pkt_len = tx->header.length + sizeof(ENIPEncapHdr); |
337 | 891k | SCLogDebug("ENIPParse packet len %d", pkt_len); |
338 | 891k | if (pkt_len > input_len) |
339 | 534k | { |
340 | 534k | SCLogDebug("Invalid packet length"); |
341 | 534k | break; |
342 | 534k | } |
343 | | |
344 | 356k | input += pkt_len; |
345 | 356k | input_len -= pkt_len; |
346 | | //SCLogDebug("remaining %d", input_len); |
347 | | |
348 | 356k | if (input_len < sizeof(ENIPEncapHdr)) |
349 | 7.00k | { |
350 | | //SCLogDebug("Not enough data"); //not enough data for ENIP |
351 | 7.00k | break; |
352 | 7.00k | } |
353 | 356k | } |
354 | | |
355 | 541k | SCReturnStruct(APP_LAYER_OK); |
356 | 541k | } |
357 | | |
358 | | static AppLayerResult ENIPParseRequest(Flow *f, void *state, AppLayerParserState *pstate, |
359 | | StreamSlice stream_slice, void *local_data) |
360 | 270k | { |
361 | 270k | return ENIPParse(f, state, pstate, stream_slice, local_data, STREAM_TOSERVER); |
362 | 270k | } |
363 | | |
364 | | static AppLayerResult ENIPParseResponse(Flow *f, void *state, AppLayerParserState *pstate, |
365 | | StreamSlice stream_slice, void *local_data) |
366 | 271k | { |
367 | 271k | return ENIPParse(f, state, pstate, stream_slice, local_data, STREAM_TOCLIENT); |
368 | 271k | } |
369 | | |
370 | 1.00k | #define ENIP_LEN_REGISTER_SESSION 4 // protocol u16, options u16 |
371 | | |
372 | | static uint16_t ENIPProbingParser(Flow *f, uint8_t direction, |
373 | | const uint8_t *input, uint32_t input_len, uint8_t *rdir) |
374 | 12.0k | { |
375 | | // SCLogDebug("ENIPProbingParser %d", input_len); |
376 | 12.0k | if (input_len < sizeof(ENIPEncapHdr)) |
377 | 4.75k | { |
378 | 4.75k | SCLogDebug("length too small to be a ENIP header"); |
379 | 4.75k | return ALPROTO_UNKNOWN; |
380 | 4.75k | } |
381 | 7.24k | uint16_t cmd; |
382 | 7.24k | uint16_t enip_len; |
383 | 7.24k | uint32_t status; |
384 | 7.24k | uint32_t option; |
385 | 7.24k | uint16_t nbitems; |
386 | | |
387 | 7.24k | int ret = ByteExtractUint32( |
388 | 7.24k | &status, BYTE_LITTLE_ENDIAN, sizeof(uint32_t), (const uint8_t *)(input + 8)); |
389 | 7.24k | if (ret < 0) { |
390 | 0 | return ALPROTO_FAILED; |
391 | 0 | } |
392 | 7.24k | switch (status) { |
393 | 3.62k | case SUCCESS: |
394 | 3.92k | case INVALID_CMD: |
395 | 4.29k | case NO_RESOURCES: |
396 | 4.68k | case INCORRECT_DATA: |
397 | 5.36k | case INVALID_SESSION: |
398 | 6.11k | case INVALID_LENGTH: |
399 | 6.32k | case UNSUPPORTED_PROT_REV: |
400 | 6.75k | case ENCAP_HEADER_ERROR: |
401 | 6.75k | break; |
402 | 495 | default: |
403 | 495 | return ALPROTO_FAILED; |
404 | 7.24k | } |
405 | 6.75k | ret = ByteExtractUint16(&cmd, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input)); |
406 | 6.75k | if(ret < 0) { |
407 | 0 | return ALPROTO_FAILED; |
408 | 0 | } |
409 | 6.75k | ret = ByteExtractUint32( |
410 | 6.75k | &option, BYTE_LITTLE_ENDIAN, sizeof(uint32_t), (const uint8_t *)(input + 20)); |
411 | 6.75k | if (ret < 0) { |
412 | 0 | return ALPROTO_FAILED; |
413 | 0 | } |
414 | 6.75k | ret = ByteExtractUint16( |
415 | 6.75k | &enip_len, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input + 2)); |
416 | 6.75k | if (ret < 0) { |
417 | 0 | return ALPROTO_FAILED; |
418 | 0 | } |
419 | | |
420 | | //ok for all the known commands |
421 | 6.75k | switch(cmd) { |
422 | 1.41k | case NOP: |
423 | 1.41k | if (option != 0) { |
424 | 59 | return ALPROTO_FAILED; |
425 | 59 | } |
426 | 1.35k | break; |
427 | 1.35k | case REGISTER_SESSION: |
428 | 204 | if (enip_len != ENIP_LEN_REGISTER_SESSION) { |
429 | 10 | return ALPROTO_FAILED; |
430 | 10 | } |
431 | 194 | break; |
432 | 402 | case UNREGISTER_SESSION: |
433 | 402 | if (enip_len != ENIP_LEN_REGISTER_SESSION && enip_len != 0) { |
434 | | // 0 for request and 4 for response |
435 | 14 | return ALPROTO_FAILED; |
436 | 14 | } |
437 | 388 | break; |
438 | 388 | case LIST_SERVICES: |
439 | 509 | case LIST_IDENTITY: |
440 | 785 | case SEND_RR_DATA: |
441 | 1.18k | case SEND_UNIT_DATA: |
442 | 1.38k | case INDICATE_STATUS: |
443 | 1.57k | case CANCEL: |
444 | 1.57k | break; |
445 | 3.11k | case LIST_INTERFACES: |
446 | 3.11k | if (input_len < sizeof(ENIPEncapHdr) + 2) { |
447 | 59 | SCLogDebug("length too small to be a ENIP LIST_INTERFACES"); |
448 | 59 | return ALPROTO_UNKNOWN; |
449 | 59 | } |
450 | 3.05k | ret = ByteExtractUint16( |
451 | 3.05k | &nbitems, BYTE_LITTLE_ENDIAN, sizeof(uint16_t), (const uint8_t *)(input)); |
452 | 3.05k | if(ret < 0) { |
453 | 0 | return ALPROTO_FAILED; |
454 | 0 | } |
455 | 3.05k | if (enip_len < sizeof(ENIPEncapHdr) + 2 * (size_t)nbitems) { |
456 | 7 | return ALPROTO_FAILED; |
457 | 7 | } |
458 | 3.05k | break; |
459 | 3.05k | default: |
460 | 38 | return ALPROTO_FAILED; |
461 | 6.75k | } |
462 | 6.56k | return ALPROTO_ENIP; |
463 | 6.75k | } |
464 | | |
465 | | static AppLayerGetTxIterTuple ENIPGetTxIterator(const uint8_t ipproto, const AppProto alproto, |
466 | | void *alstate, uint64_t min_tx_id, uint64_t max_tx_id, AppLayerGetTxIterState *state) |
467 | 676k | { |
468 | 676k | ENIPState *enip_state = (ENIPState *)alstate; |
469 | 676k | AppLayerGetTxIterTuple no_tuple = { NULL, 0, false }; |
470 | 676k | if (enip_state) { |
471 | 676k | ENIPTransaction *tx_ptr; |
472 | 676k | if (state->un.ptr == NULL) { |
473 | 563k | tx_ptr = TAILQ_FIRST(&enip_state->tx_list); |
474 | 563k | } else { |
475 | 113k | tx_ptr = (ENIPTransaction *)state->un.ptr; |
476 | 113k | } |
477 | 676k | if (tx_ptr) { |
478 | 676k | while (tx_ptr->tx_num < min_tx_id + 1) { |
479 | 59 | tx_ptr = TAILQ_NEXT(tx_ptr, next); |
480 | 59 | if (!tx_ptr) { |
481 | 29 | return no_tuple; |
482 | 29 | } |
483 | 59 | } |
484 | 676k | if (tx_ptr->tx_num >= max_tx_id + 1) { |
485 | 0 | return no_tuple; |
486 | 0 | } |
487 | 676k | state->un.ptr = TAILQ_NEXT(tx_ptr, next); |
488 | 676k | AppLayerGetTxIterTuple tuple = { |
489 | 676k | .tx_ptr = tx_ptr, |
490 | 676k | .tx_id = tx_ptr->tx_num - 1, |
491 | 676k | .has_next = (state->un.ptr != NULL), |
492 | 676k | }; |
493 | 676k | return tuple; |
494 | 676k | } |
495 | 676k | } |
496 | 840 | return no_tuple; |
497 | 676k | } |
498 | | |
499 | | /** |
500 | | * \brief Function to register the ENIP protocol parsers and other functions |
501 | | */ |
502 | | void RegisterENIPUDPParsers(void) |
503 | 34 | { |
504 | 34 | SCEnter(); |
505 | 34 | const char *proto_name = "enip"; |
506 | | |
507 | 34 | if (AppLayerProtoDetectConfProtoDetectionEnabledDefault("udp", proto_name, false)) { |
508 | 34 | AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name); |
509 | | |
510 | 34 | if (RunmodeIsUnittests()) |
511 | 0 | { |
512 | 0 | AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP, |
513 | 0 | 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL); |
514 | |
|
515 | 0 | AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", ALPROTO_ENIP, |
516 | 0 | 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL); |
517 | |
|
518 | 0 | } else |
519 | 34 | { |
520 | 34 | if (!AppLayerProtoDetectPPParseConfPorts("udp", IPPROTO_UDP, |
521 | 34 | proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), |
522 | 34 | ENIPProbingParser, ENIPProbingParser)) |
523 | 1 | { |
524 | 1 | SCLogDebug( |
525 | 1 | "no ENIP UDP config found enabling ENIP detection on port 44818."); |
526 | | |
527 | 1 | AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", |
528 | 1 | ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, |
529 | 1 | ENIPProbingParser, NULL); |
530 | | |
531 | 1 | AppLayerProtoDetectPPRegister(IPPROTO_UDP, "44818", |
532 | 1 | ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, |
533 | 1 | ENIPProbingParser, NULL); |
534 | 1 | } |
535 | 34 | } |
536 | | |
537 | 34 | } else { |
538 | 0 | SCLogConfig("Protocol detection and parser disabled for %s protocol.", |
539 | 0 | proto_name); |
540 | 0 | return; |
541 | 0 | } |
542 | | |
543 | 34 | if (AppLayerParserConfParserEnabled("udp", proto_name)) |
544 | 34 | { |
545 | 34 | AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP, STREAM_TOSERVER, ENIPParseRequest); |
546 | 34 | AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_ENIP, STREAM_TOCLIENT, ENIPParseResponse); |
547 | | |
548 | 34 | AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_ENIP, |
549 | 34 | ENIPStateAlloc, ENIPStateFree); |
550 | | |
551 | 34 | AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTx); |
552 | 34 | AppLayerParserRegisterGetTxIterator(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxIterator); |
553 | 34 | AppLayerParserRegisterTxDataFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxData); |
554 | 34 | AppLayerParserRegisterStateDataFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetStateData); |
555 | 34 | AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetTxCnt); |
556 | 34 | AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateTransactionFree); |
557 | | |
558 | 34 | AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_ENIP, ENIPGetAlstateProgress); |
559 | 34 | AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_ENIP, 1, 1); |
560 | | |
561 | 34 | AppLayerParserRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfo); |
562 | 34 | AppLayerParserRegisterGetEventInfoById(IPPROTO_UDP, ALPROTO_ENIP, ENIPStateGetEventInfoById); |
563 | | |
564 | 34 | AppLayerParserRegisterParserAcceptableDataDirection( |
565 | 34 | IPPROTO_UDP, ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT); |
566 | 34 | } else |
567 | 0 | { |
568 | 0 | SCLogInfo( |
569 | 0 | "Parsed disabled for %s protocol. Protocol detection" "still on.", |
570 | 0 | proto_name); |
571 | 0 | } |
572 | | |
573 | | #ifdef UNITTESTS |
574 | | AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_ENIP, ENIPParserRegisterTests); |
575 | | #endif |
576 | | |
577 | 34 | SCReturn; |
578 | 34 | } |
579 | | |
580 | | /** |
581 | | * \brief Function to register the ENIP protocol parsers and other functions |
582 | | */ |
583 | | void RegisterENIPTCPParsers(void) |
584 | 34 | { |
585 | 34 | SCEnter(); |
586 | 34 | const char *proto_name = "enip"; |
587 | | |
588 | 34 | if (AppLayerProtoDetectConfProtoDetectionEnabledDefault("tcp", proto_name, false)) { |
589 | 34 | AppLayerProtoDetectRegisterProtocol(ALPROTO_ENIP, proto_name); |
590 | | |
591 | 34 | if (RunmodeIsUnittests()) |
592 | 0 | { |
593 | 0 | AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP, |
594 | 0 | 0, sizeof(ENIPEncapHdr), STREAM_TOSERVER, ENIPProbingParser, NULL); |
595 | |
|
596 | 0 | AppLayerProtoDetectPPRegister(IPPROTO_TCP, "44818", ALPROTO_ENIP, |
597 | 0 | 0, sizeof(ENIPEncapHdr), STREAM_TOCLIENT, ENIPProbingParser, NULL); |
598 | |
|
599 | 0 | } else |
600 | 34 | { |
601 | 34 | if (!AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP, |
602 | 34 | proto_name, ALPROTO_ENIP, 0, sizeof(ENIPEncapHdr), |
603 | 34 | ENIPProbingParser, ENIPProbingParser)) |
604 | 1 | { |
605 | 1 | return; |
606 | 1 | } |
607 | 34 | } |
608 | | |
609 | 34 | } else { |
610 | 0 | SCLogDebug("Protocol detection and parser disabled for %s protocol.", |
611 | 0 | proto_name); |
612 | 0 | return; |
613 | 0 | } |
614 | | |
615 | 33 | if (AppLayerParserConfParserEnabled("tcp", proto_name)) |
616 | 33 | { |
617 | 33 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP, STREAM_TOSERVER, ENIPParseRequest); |
618 | 33 | AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_ENIP, STREAM_TOCLIENT, ENIPParseResponse); |
619 | 33 | AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_ENIP, |
620 | 33 | ENIPStateAlloc, ENIPStateFree); |
621 | | |
622 | 33 | AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTx); |
623 | 33 | AppLayerParserRegisterGetTxIterator(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxIterator); |
624 | 33 | AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxData); |
625 | 33 | AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetStateData); |
626 | 33 | AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetTxCnt); |
627 | 33 | AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateTransactionFree); |
628 | | |
629 | 33 | AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_ENIP, ENIPGetAlstateProgress); |
630 | 33 | AppLayerParserRegisterStateProgressCompletionStatus(ALPROTO_ENIP, 1, 1); |
631 | | |
632 | 33 | AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_ENIP, ENIPStateGetEventInfo); |
633 | | |
634 | 33 | AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, |
635 | 33 | ALPROTO_ENIP, STREAM_TOSERVER | STREAM_TOCLIENT); |
636 | | |
637 | | /* This parser accepts gaps. */ |
638 | 33 | AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_ENIP, |
639 | 33 | APP_LAYER_PARSER_OPT_ACCEPT_GAPS); |
640 | | |
641 | 33 | AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_ENIP, 0); |
642 | 33 | } else |
643 | 0 | { |
644 | 0 | SCLogConfig("Parser disabled for %s protocol. Protocol detection still on.", |
645 | 0 | proto_name); |
646 | 0 | } |
647 | | |
648 | | #ifdef UNITTESTS |
649 | | AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_ENIP, ENIPParserRegisterTests); |
650 | | #endif |
651 | | |
652 | 33 | SCReturn; |
653 | 34 | } |
654 | | |
655 | | /* UNITTESTS */ |
656 | | #ifdef UNITTESTS |
657 | | #include "flow-util.h" |
658 | | #include "stream-tcp.h" |
659 | | |
660 | | static uint8_t listIdentity[] = {/* List ID */ 0x63, 0x00, |
661 | | /* Length */ 0x00, 0x00, |
662 | | /* Session */ 0x00, 0x00, 0x00, 0x00, |
663 | | /* Status */ 0x00, 0x00, 0x00, 0x00, |
664 | | /* Delay*/ 0x00, |
665 | | /* Context */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
666 | | /* Quantity of coils */ 0x00, 0x00, 0x00, 0x00, 0x00}; |
667 | | |
668 | | /** |
669 | | * \brief Test if ENIP Packet matches signature |
670 | | */ |
671 | | static int ALDecodeENIPTest(void) |
672 | | { |
673 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
674 | | Flow f; |
675 | | TcpSession ssn; |
676 | | |
677 | | memset(&f, 0, sizeof(f)); |
678 | | memset(&ssn, 0, sizeof(ssn)); |
679 | | |
680 | | f.protoctx = (void *)&ssn; |
681 | | f.proto = IPPROTO_TCP; |
682 | | f.alproto = ALPROTO_ENIP; |
683 | | |
684 | | StreamTcpInitConfig(true); |
685 | | |
686 | | int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_ENIP, STREAM_TOSERVER, |
687 | | listIdentity, sizeof(listIdentity)); |
688 | | FAIL_IF(r != 0); |
689 | | |
690 | | ENIPState *enip_state = f.alstate; |
691 | | FAIL_IF_NULL(enip_state); |
692 | | |
693 | | ENIPTransaction *tx = ENIPGetTx(enip_state, 0); |
694 | | FAIL_IF_NULL(tx); |
695 | | |
696 | | FAIL_IF(tx->header.command != 99); |
697 | | |
698 | | AppLayerParserThreadCtxFree(alp_tctx); |
699 | | StreamTcpFreeConfig(true); |
700 | | FLOW_DESTROY(&f); |
701 | | |
702 | | PASS; |
703 | | } |
704 | | |
705 | | #endif /* UNITTESTS */ |
706 | | |
707 | | void ENIPParserRegisterTests(void) |
708 | 0 | { |
709 | | #ifdef UNITTESTS |
710 | | UtRegisterTest("ALDecodeENIPTest", ALDecodeENIPTest); |
711 | | #endif /* UNITTESTS */ |
712 | 0 | } |