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