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