suricata
app-layer-enip-common.c
Go to the documentation of this file.
1 /* Copyright (C) 2015-2022 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 common code
24  *
25  */
26 
27 #include "suricata-common.h"
28 #include "util-unittest.h"
29 #include "util-unittest-helper.h"
30 #include "detect-parse.h"
31 #include "detect-engine.h"
32 #include "util-byte.h"
33 #include "pkt-var.h"
34 #include "util-profiling.h"
35 
36 #include "app-layer-enip-common.h"
37 
38 /**
39  * \brief Extract 8 bits and move up the offset
40  * @param res
41  * @param input
42  * @param offset
43  */
44 static int ENIPExtractUint8(uint8_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
45 {
46 
47  if (input_len < sizeof(uint8_t) || *offset > (input_len - sizeof(uint8_t)))
48  {
49  SCLogDebug("ENIPExtractUint8: Parsing beyond payload length");
50  return 0;
51  }
52 
53  *res = *(input + *offset);
54  *offset += sizeof(uint8_t);
55  return 1;
56 }
57 
58 /**
59  * \brief Extract 16 bits and move up the offset
60  * @param res
61  * @param input
62  * @param offset
63  */
64 static int ENIPExtractUint16(uint16_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
65 {
66 
67  if (input_len < sizeof(uint16_t) || *offset > (input_len - sizeof(uint16_t))) {
68  SCLogDebug("ENIPExtractUint16: Parsing beyond payload length");
69  return 0;
70  }
71 
72  if (ByteExtractUint16(res, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
73  (const uint8_t *)(input + *offset)) == -1) {
74  return 0;
75  }
76 
77  *offset += sizeof(uint16_t);
78  return 1;
79 }
80 
81 /**
82  * \brief Extract 32 bits and move up the offset
83  * @param res
84  * @param input
85  * @param offset
86  */
87 static int ENIPExtractUint32(uint32_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
88 {
89 
90  if (input_len < sizeof(uint32_t) || *offset > (input_len - sizeof(uint32_t)))
91  {
92  SCLogDebug("ENIPExtractUint32: Parsing beyond payload length");
93  return 0;
94  }
95 
96  if (ByteExtractUint32(res, BYTE_LITTLE_ENDIAN, sizeof(uint32_t),
97  (const uint8_t *)(input + *offset)) == -1) {
98  return 0;
99  }
100 
101  *offset += sizeof(uint32_t);
102  return 1;
103 }
104 
105 /**
106  * \brief Extract 64 bits and move up the offset
107  * @param res
108  * @param input
109  * @param offset
110  */
111 static int ENIPExtractUint64(uint64_t *res, const uint8_t *input, uint16_t *offset, uint32_t input_len)
112 {
113 
114  if (input_len < sizeof(uint64_t) || *offset > (input_len - sizeof(uint64_t)))
115  {
116  SCLogDebug("ENIPExtractUint64: Parsing beyond payload length");
117  return 0;
118  }
119 
120  if (ByteExtractUint64(res, BYTE_LITTLE_ENDIAN, sizeof(uint64_t),
121  (const uint8_t *)(input + *offset)) == -1) {
122  return 0;
123  }
124 
125  *offset += sizeof(uint64_t);
126  return 1;
127 }
128 
129 
130 /**
131  * \brief Create service entry, add to transaction
132  * @param tx Transaction
133  * @return service entry
134  */
135 static CIPServiceEntry *CIPServiceAlloc(ENIPTransaction *tx)
136 {
137 
139  sizeof(CIPServiceEntry));
140  if (unlikely(svc == NULL))
141  return NULL;
142 
143  TAILQ_INIT(&svc->segment_list);
144  TAILQ_INIT(&svc->attrib_list);
145 
146  TAILQ_INSERT_TAIL(&tx->service_list, svc, next);
147  tx->service_count++;
148  return svc;
149 
150 }
151 
152 #if 0
153 /**
154  * \brief Delete service entry
155  */
156 
157 static void CIPServiceFree(void *s)
158 {
159  SCEnter();
160  if (s)
161  {
162  CIPServiceEntry *svc = (CIPServiceEntry *) s;
163 
164  SegmentEntry *seg = NULL;
165  while ((seg = TAILQ_FIRST(&svc->segment_list)))
166  {
167  TAILQ_REMOVE(&svc->segment_list, seg, next);
168  SCFree(seg);
169  }
170 
171  AttributeEntry *attr = NULL;
172  while ((attr = TAILQ_FIRST(&svc->attrib_list)))
173  {
174  TAILQ_REMOVE(&svc->attrib_list, attr, next);
175  SCFree(attr);
176  }
177 
178  SCFree(s);
179  }
180  SCReturn;
181 }
182 #endif
183 
184 /**
185  * \brief Decode ENIP Encapsulation Header
186  * @param input, input_len data stream
187  * @param enip_data stores data from Packet
188  * @return 1 Packet ok
189  * @return 0 Packet has errors
190  */
191 int DecodeENIPPDU(const uint8_t *input, uint32_t input_len,
192  ENIPTransaction *enip_data)
193 {
194  int ret = 1;
195 
196  uint16_t offset = 0; //byte offset
197 
198  //Decode Encapsulation Header
199  uint16_t cmd;
200  uint16_t len;
201  uint32_t session;
202  uint32_t status;
203  uint64_t context;
204  uint32_t option;
205  if (ENIPExtractUint16(&cmd, input, &offset, input_len) != 1)
206  {
207  return 0;
208  }
209  if (ENIPExtractUint16(&len, input, &offset, input_len) != 1)
210  {
211  return 0;
212  }
213  if (ENIPExtractUint32(&session, input, &offset, input_len) != 1)
214  {
215  return 0;
216  }
217  if (ENIPExtractUint32(&status, input, &offset, input_len) != 1)
218  {
219  return 0;
220  }
221  if (ENIPExtractUint64(&context, input, &offset, input_len) != 1)
222  {
223  return 0;
224  }
225  if (ENIPExtractUint32(&option, input, &offset, input_len) != 1)
226  {
227  return 0;
228  }
229 
230  enip_data->header.command = cmd;
231  enip_data->header.length = len;
232  enip_data->header.session = session;
233  enip_data->header.status = status;
234  enip_data->header.context = context;
235  enip_data->header.option = option;
236 
237  switch (enip_data->header.command)
238  {
239  case NOP:
240  SCLogDebug("DecodeENIP - NOP");
241  break;
242  case LIST_SERVICES:
243  SCLogDebug("DecodeENIP - LIST_SERVICES");
244  break;
245  case LIST_IDENTITY:
246  SCLogDebug("DecodeENIP - LIST_IDENTITY");
247  break;
248  case LIST_INTERFACES:
249  SCLogDebug("DecodeENIP - LIST_INTERFACES");
250  break;
251  case REGISTER_SESSION:
252  SCLogDebug("DecodeENIP - REGISTER_SESSION");
253  break;
254  case UNREGISTER_SESSION:
255  SCLogDebug("DecodeENIP - UNREGISTER_SESSION");
256  break;
257  case SEND_RR_DATA:
258  SCLogDebug(
259  "DecodeENIP - SEND_RR_DATA - parse Common Packet Format");
260  ret = DecodeCommonPacketFormatPDU(input, input_len, enip_data,
261  offset);
262  break;
263  case SEND_UNIT_DATA:
264  SCLogDebug(
265  "DecodeENIP - SEND UNIT DATA - parse Common Packet Format");
266  ret = DecodeCommonPacketFormatPDU(input, input_len, enip_data,
267  offset);
268  break;
269  case INDICATE_STATUS:
270  SCLogDebug("DecodeENIP - INDICATE_STATUS");
271  break;
272  case CANCEL:
273  SCLogDebug("DecodeENIP - CANCEL");
274  break;
275  default:
276  SCLogDebug("DecodeENIP - UNSUPPORTED COMMAND 0x%x",
277  enip_data->header.command);
278  }
279 
280  return ret;
281 }
282 
283 
284 /**
285  * \brief Decode Common Packet Format
286  * @param input, input_len data stream
287  * @param enip_data stores data from Packet
288  * @param offset current point in the packet
289  * @return 1 Packet ok
290  * @return 0 Packet has errors
291  */
292 int DecodeCommonPacketFormatPDU(const uint8_t *input, uint32_t input_len,
293  ENIPTransaction *enip_data, uint16_t offset)
294 {
295 
296  if (enip_data->header.length < sizeof(ENIPEncapDataHdr))
297  {
298  SCLogDebug("DecodeCommonPacketFormat: Malformed ENIP packet");
299  return 0;
300  }
301 
302  uint32_t handle;
303  uint16_t timeout;
304  uint16_t count;
305  if (ENIPExtractUint32(&handle, input, &offset, input_len) != 1)
306  {
307  return 0;
308  }
309  if (ENIPExtractUint16(&timeout, input, &offset, input_len) != 1)
310  {
311  return 0;
312  }
313  if (ENIPExtractUint16(&count, input, &offset, input_len) != 1)
314  {
315  return 0;
316  }
317  enip_data->encap_data_header.interface_handle = handle;
318  enip_data->encap_data_header.timeout = timeout;
319  enip_data->encap_data_header.item_count = count;
320 
321  uint16_t address_type;
322  uint16_t address_length; //length of connection id in bytes
323  uint32_t address_connectionid = 0;
324  uint32_t address_sequence = 0;
325 
326  if (ENIPExtractUint16(&address_type, input, &offset, input_len) != 1)
327  {
328  return 0;
329  }
330  if (ENIPExtractUint16(&address_length, input, &offset, input_len) != 1)
331  {
332  return 0;
333  }
334 
335  //depending on addr type, get connection id, sequence if needed. Can also use addr length too?
336  if (address_type == CONNECTION_BASED)
337  { //get 4 byte connection id
338  if (ENIPExtractUint32(&address_connectionid, input, &offset, input_len) != 1)
339  {
340  return 0;
341  }
342  } else if (address_type == SEQUENCE_ADDR_ITEM)
343  { // get 4 byte connection id and 4 byte sequence
344  if (ENIPExtractUint32(&address_connectionid, input, &offset, input_len) != 1)
345  {
346  return 0;
347  }
348  if (ENIPExtractUint32(&address_sequence, input, &offset, input_len) != 1)
349  {
350  return 0;
351  }
352  }
353 
354  enip_data->encap_addr_item.type = address_type;
355  enip_data->encap_addr_item.length = address_length;
356  enip_data->encap_addr_item.conn_id = address_connectionid;
357  enip_data->encap_addr_item.sequence_num = address_sequence;
358 
359  uint16_t data_type;
360  uint16_t data_length; //length of data in bytes
361  uint16_t data_sequence_count;
362 
363  if (ENIPExtractUint16(&data_type, input, &offset, input_len) != 1)
364  {
365  return 0;
366  }
367  if (ENIPExtractUint16(&data_length, input, &offset, input_len) != 1)
368  {
369  return 0;
370  }
371 
372  enip_data->encap_data_item.type = data_type;
373  enip_data->encap_data_item.length = data_length;
374 
375  if (enip_data->encap_data_item.type == CONNECTED_DATA_ITEM)
376  { //connected data items have seq number
377  if (ENIPExtractUint16(&data_sequence_count, input, &offset, input_len) != 1)
378  {
379  return 0;
380  }
381  enip_data->encap_data_item.sequence_count = data_sequence_count;
382  }
383 
384  switch (enip_data->encap_data_item.type) {
385  case CONNECTED_DATA_ITEM:
386  SCLogDebug(
387  "DecodeCommonPacketFormat - CONNECTED DATA ITEM - parse CIP");
388  DecodeCIPPDU(input, input_len, enip_data, offset);
389  break;
391  SCLogDebug("DecodeCommonPacketFormat - UNCONNECTED DATA ITEM");
392  DecodeCIPPDU(input, input_len, enip_data, offset);
393  break;
394  default:
395  SCLogDebug("DecodeCommonPacketFormat - UNKNOWN TYPE 0x%x",
396  enip_data->encap_data_item.type);
397  return 0;
398  }
399 
400  return 1;
401 }
402 
403 /**
404  * \brief Decode CIP packet
405  * @param input, input_len data stream
406  * @param enip_data stores data from Packet
407  * @param offset current point in the packet
408  * @return 1 Packet ok
409  * @return 0 Packet has errors
410  */
411 
412 int DecodeCIPPDU(const uint8_t *input, uint32_t input_len,
413  ENIPTransaction *enip_data, uint16_t offset)
414 {
415  int ret = 1;
416 
417  if (enip_data->encap_data_item.length == 0)
418  {
419  SCLogDebug("DecodeCIP: No CIP Data");
420  return 0;
421  }
422 
423  if (offset > (input_len - sizeof(uint8_t)))
424  {
425  SCLogDebug("DecodeCIP: Parsing beyond payload length");
426  return 0;
427  }
428 
429  uint8_t service = 0;
430  service = *(input + offset);
431 
432  //SCLogDebug("CIP Service 0x%x", service);
433 
434  //use service code first bit to determine request/response, no need to save or push offset
435  if (service >> 7)
436  {
437  ret = DecodeCIPResponsePDU(input, input_len, enip_data, offset);
438  } else
439  {
440  ret = DecodeCIPRequestPDU(input, input_len, enip_data, offset);
441  }
442 
443  return ret;
444 }
445 
446 
447 
448 /**
449  * \brief Decode CIP Request
450  * @param input, input_len data stream
451  * @param enip_data stores data from Packet
452  * @param offset current point in the packet
453  * @return 1 Packet ok
454  * @return 0 Packet has errors
455  */
456 int DecodeCIPRequestPDU(const uint8_t *input, uint32_t input_len,
457  ENIPTransaction *enip_data, uint16_t offset)
458 {
459  int ret = 1;
460 
461  if (enip_data->encap_data_item.length < sizeof(CIPReqHdr))
462  {
463  SCLogDebug("DecodeCIPRequest - Malformed CIP Data");
464  return 0;
465  }
466 
467  uint8_t service = 0; //<-----CIP SERVICE
468  uint8_t path_size = 0;
469 
470  if (ENIPExtractUint8(&service, input, &offset, input_len) != 1)
471  {
472  return 0;
473  }
474  if (ENIPExtractUint8(&path_size, input, &offset, input_len) != 1)
475  {
476  return 0;
477  }
478 
479  if (service > MAX_CIP_SERVICE)
480  { // service codes of value 0x80 or greater are not permitted because in the CIP protocol the highest order bit is used to flag request(0)/response(1)
481  SCLogDebug("DecodeCIPRequest - INVALID CIP SERVICE 0x%x", service);
482  return 0;
483  }
484 
485  //reached maximum number of services
486  if (enip_data->service_count > 32)
487  {
488  SCLogDebug("DecodeCIPRequest: Maximum services reached");
489  return 0;
490  }
491 
492  //save CIP data
493  CIPServiceEntry *node = CIPServiceAlloc(enip_data);
494  if (node == NULL)
495  {
496  SCLogDebug("DecodeCIPRequest: Unable to create CIP service");
497  return 0;
498  }
499  node->direction = 0;
500  node->service = service;
501  node->request.path_size = path_size;
502  node->request.path_offset = offset;
503  // SCLogDebug("DecodeCIPRequestPDU: service 0x%x size %d", node->service,
504  // node->request.path_size);
505 
506  DecodeCIPRequestPathPDU(input, input_len, node, offset);
507 
508  offset += path_size * sizeof(uint16_t); //move offset past pathsize
509 
510  //list of CIP services is large and can be vendor specific, store CIP service anyways and let the rule decide the action
511  switch (service)
512  {
513  case CIP_RESERVED:
514  SCLogDebug("DecodeCIPRequest - CIP_RESERVED");
515  break;
516  case CIP_GET_ATTR_ALL:
517  SCLogDebug("DecodeCIPRequest - CIP_GET_ATTR_ALL");
518  break;
519  case CIP_GET_ATTR_LIST:
520  SCLogDebug("DecodeCIPRequest - CIP_GET_ATTR_LIST");
521  break;
522  case CIP_SET_ATTR_LIST:
523  SCLogDebug("DecodeCIPRequest - CIP_SET_ATTR_LIST");
524  break;
525  case CIP_RESET:
526  SCLogDebug("DecodeCIPRequest - CIP_RESET");
527  break;
528  case CIP_START:
529  SCLogDebug("DecodeCIPRequest - CIP_START");
530  break;
531  case CIP_STOP:
532  SCLogDebug("DecodeCIPRequest - CIP_STOP");
533  break;
534  case CIP_CREATE:
535  SCLogDebug("DecodeCIPRequest - CIP_CREATE");
536  break;
537  case CIP_DELETE:
538  SCLogDebug("DecodeCIPRequest - CIP_DELETE");
539  break;
540  case CIP_MSP:
541  SCLogDebug("DecodeCIPRequest - CIP_MSP");
542  DecodeCIPRequestMSPPDU(input, input_len, enip_data, offset);
543  break;
544  case CIP_APPLY_ATTR:
545  SCLogDebug("DecodeCIPRequest - CIP_APPLY_ATTR");
546  break;
547  case CIP_KICK_TIMER:
548  SCLogDebug("DecodeCIPRequest - CIP_KICK_TIMER");
549  break;
550  case CIP_OPEN_CONNECTION:
551  SCLogDebug("DecodeCIPRequest - CIP_OPEN_CONNECTION");
552  break;
553  case CIP_CHANGE_START:
554  SCLogDebug("DecodeCIPRequest - CIP_CHANGE_START");
555  break;
556  case CIP_GET_STATUS:
557  SCLogDebug("DecodeCIPRequest - CIP_GET_STATUS");
558  break;
559  default:
560  SCLogDebug("DecodeCIPRequest - CIP SERVICE 0x%x", service);
561  }
562 
563  return ret;
564 }
565 
566 /**
567  * \brief Decode CIP Request Path
568  * @param input, input_len data stream
569  * @param enip_data stores data from Packet
570  * @param offset current point in the packet
571  * @param cipserviced the cip service rule
572  * @return 1 Packet matches
573  * @return 0 Packet not match
574  */
575 int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len,
576  CIPServiceEntry *node, uint16_t offset)
577 {
578  //SCLogDebug("DecodeCIPRequestPath: service 0x%x size %d length %d",
579  // node->service, node->request.path_size, input_len);
580 
581  if (node->request.path_size < 1)
582  {
583  //SCLogDebug("DecodeCIPRequestPath: empty path or CIP Response");
584  return 0;
585  }
586 
587  int bytes_remain = node->request.path_size;
588 
589  uint8_t reserved; //unused byte reserved by ODVA
590 
591  //8 bit fields
592  uint8_t req_path_instance8;
593  uint8_t req_path_attr8;
594 
595  //16 bit fields
596  uint16_t req_path_class16;
597  uint16_t req_path_instance16;
598 
599  uint16_t class = 0;
600 
601  SegmentEntry *seg = NULL;
602 
603  while (bytes_remain > 0)
604  {
605  uint8_t segment = 0;
606  if (ENIPExtractUint8(&segment, input, &offset, input_len) != 1)
607  {
608  return 0;
609  }
610  switch (segment)
611  { //assume order is class then instance. Can have multiple
612  case PATH_CLASS_8BIT: {
613  uint8_t req_path_class8 = 0;
614  if (ENIPExtractUint8(&req_path_class8, input, &offset, input_len) != 1) {
615  return 0;
616  }
617  class = (uint16_t) req_path_class8;
618  SCLogDebug("DecodeCIPRequestPathPDU: 8bit class 0x%x", class);
619 
620  seg = SCMalloc(sizeof(SegmentEntry));
621  if (unlikely(seg == NULL))
622  return 0;
623  seg->segment = segment;
624  seg->value = class;
625  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
626 
627  bytes_remain--;
628  break;
629  }
630  case PATH_INSTANCE_8BIT:
631  if (ENIPExtractUint8(&req_path_instance8, input, &offset, input_len) != 1)
632  {
633  return 0;
634  }
635  //skip instance, don't need to store
636  bytes_remain--;
637  break;
638  case PATH_ATTR_8BIT: //single attribute
639  if (ENIPExtractUint8(&req_path_attr8, input, &offset, input_len) != 1)
640  {
641  return 0;
642  }
643  //uint16_t attrib = (uint16_t) req_path_attr8;
644  //SCLogDebug("DecodeCIPRequestPath: 8bit attr 0x%x", attrib);
645 
646  seg = SCMalloc(sizeof(SegmentEntry));
647  if (unlikely(seg == NULL))
648  return 0;
649  seg->segment = segment;
650  seg->value = class;
651  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
652 
653  bytes_remain--;
654  break;
655  case PATH_CLASS_16BIT:
656  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) //skip reserved
657  {
658  return 0;
659  }
660  if (ENIPExtractUint16(&req_path_class16, input, &offset, input_len) != 1)
661  {
662  return 0;
663  }
664  class = req_path_class16;
665  SCLogDebug("DecodeCIPRequestPath: 16bit class 0x%x", class);
666 
667  seg = SCMalloc(sizeof(SegmentEntry));
668  if (unlikely(seg == NULL))
669  return 0;
670  seg->segment = segment;
671  seg->value = class;
672  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
673  if (bytes_remain >= 2)
674  {
675  bytes_remain = bytes_remain - 2;
676  } else
677  {
678  bytes_remain = 0;
679  }
680  break;
681  case PATH_INSTANCE_16BIT:
682  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) // skip reserved
683  {
684  return 0;
685  }
686  if (ENIPExtractUint16(&req_path_instance16, input, &offset, input_len) != 1)
687  {
688  return 0;
689  }
690  //skip instance, don't need to store
691  if (bytes_remain >= 2)
692  {
693  bytes_remain = bytes_remain - 2;
694  } else
695  {
696  bytes_remain = 0;
697  }
698  break;
699  default:
700  SCLogDebug(
701  "DecodeCIPRequestPath: UNKNOWN SEGMENT 0x%x service 0x%x",
702  segment, node->service);
703  return 0;
704  }
705  }
706 
707  if ((node->service == CIP_SET_ATTR_LIST) || (node->service
708  == CIP_GET_ATTR_LIST))
709  {
710  uint16_t attr_list_count;
711  uint16_t attribute;
712  //parse get/set attribute list
713 
714  if (ENIPExtractUint16(&attr_list_count, input, &offset, input_len) != 1)
715  {
716  return 0;
717  }
718  SCLogDebug("DecodeCIPRequestPathPDU: attribute list count %d",
719  attr_list_count);
720  for (int i = 0; i < attr_list_count; i++)
721  {
722  if (ENIPExtractUint16(&attribute, input, &offset, input_len) != 1)
723  {
724  return 0;
725  }
726  SCLogDebug("DecodeCIPRequestPathPDU: attribute %d", attribute);
727  //save attrs
728  AttributeEntry *attr = SCMalloc(sizeof(AttributeEntry));
729  if (unlikely(attr == NULL))
730  return 0;
731  attr->attribute = attribute;
732  TAILQ_INSERT_TAIL(&node->attrib_list, attr, next);
733 
734  }
735  }
736 
737  return 1;
738 }
739 
740 /**
741  * \brief Decode CIP Response
742  * @param input, input_len data stream
743  * @param enip_data stores data from Packet
744  * @param offset current point in the packet
745  * @return 1 Packet ok
746  * @return 0 Packet has errors
747  */
748 int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len,
749  ENIPTransaction *enip_data, uint16_t offset)
750 {
751  int ret = 1;
752 
753  if (enip_data->encap_data_item.length < sizeof(CIPRespHdr))
754  {
755  SCLogDebug("DecodeCIPResponse - Malformed CIP Data");
756  return 0;
757  }
758 
759  uint8_t service = 0; //<----CIP SERVICE
760  uint8_t reserved; //unused byte reserved by ODVA
761  uint16_t status;
762 
763  if (ENIPExtractUint8(&service, input, &offset, input_len) != 1)
764  {
765  return 0;
766  }
767  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1)
768  {
769  return 0;
770  }
771  if (ENIPExtractUint16(&status, input, &offset, input_len) != 1)
772  {
773  return 0;
774  }
775 
776  //SCLogDebug("DecodeCIPResponse: service 0x%x",service);
777  service &= 0x7f; //strip off top bit to get service code. Responses have first bit as 1
778 
779  SCLogDebug("CIP service 0x%x status 0x%x", service, status);
780 
781  //reached maximum number of services
782  if (enip_data->service_count > 32)
783  {
784  SCLogDebug("DecodeCIPRequest: Maximum services reached");
785  return 0;
786  }
787 
788  //save CIP data
789  CIPServiceEntry *node = CIPServiceAlloc(enip_data);
790  if (node == NULL)
791  {
792  SCLogDebug("DecodeCIPRequest: Unable to create CIP service");
793  return 0;
794  }
795  node->direction = 1;
796  node->service = service;
797  node->response.status = status;
798 
799  SCLogDebug("DecodeCIPResponsePDU: service 0x%x size %d", node->service,
800  node->request.path_size);
801 
802  //list of CIP services is large and can be vendor specific, store CIP service anyways and let the rule decide the action
803  switch (service)
804  {
805  case CIP_RESERVED:
806  SCLogDebug("DecodeCIPResponse - CIP_RESERVED");
807  break;
808  case CIP_GET_ATTR_ALL:
809  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_ALL");
810  break;
811  case CIP_GET_ATTR_LIST:
812  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_LIST");
813  break;
814  case CIP_SET_ATTR_LIST:
815  SCLogDebug("DecodeCIPResponse - CIP_SET_ATTR_LIST");
816  break;
817  case CIP_RESET:
818  SCLogDebug("DecodeCIPResponse - CIP_RESET");
819  break;
820  case CIP_START:
821  SCLogDebug("DecodeCIPResponse - CIP_START");
822  break;
823  case CIP_STOP:
824  SCLogDebug("DecodeCIPResponse - CIP_STOP");
825  break;
826  case CIP_CREATE:
827  SCLogDebug("DecodeCIPResponse - CIP_CREATE");
828  break;
829  case CIP_DELETE:
830  SCLogDebug("DecodeCIPResponse - CIP_DELETE");
831  break;
832  case CIP_MSP:
833  SCLogDebug("DecodeCIPResponse - CIP_MSP");
834  DecodeCIPResponseMSPPDU(input, input_len, enip_data, offset);
835  break;
836  case CIP_APPLY_ATTR:
837  SCLogDebug("DecodeCIPResponse - CIP_APPLY_ATTR");
838  break;
839  case CIP_KICK_TIMER:
840  SCLogDebug("DecodeCIPResponse - CIP_KICK_TIMER");
841  break;
842  case CIP_OPEN_CONNECTION:
843  SCLogDebug("DecodeCIPResponse - CIP_OPEN_CONNECTION");
844  break;
845  case CIP_CHANGE_START:
846  SCLogDebug("DecodeCIPResponse - CIP_CHANGE_START");
847  break;
848  case CIP_GET_STATUS:
849  SCLogDebug("DecodeCIPResponse - CIP_GET_STATUS");
850  break;
851  default:
852  SCLogDebug("DecodeCIPResponse - CIP SERVICE 0x%x", service);
853  }
854 
855  return ret;
856 }
857 
858 
859 /**
860  * \brief Decode CIP Request Multi Service Packet
861  * @param input, input_len data stream
862  * @param enip_data stores data from Packet
863  * @param offset current point in the packet
864  * @return 1 Packet ok
865  * @return 0 Packet has errors
866  */
867 int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len,
868  ENIPTransaction *enip_data, uint16_t offset)
869 {
870  int ret = 1;
871  if (offset >= (input_len - sizeof(uint16_t)))
872  {
873  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
874  return 0;
875  }
876  //use temp_offset just to grab the service offset, don't want to use and push offset
877  uint16_t temp_offset = offset;
878  uint16_t num_services;
879  if (ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
880  (const uint8_t *)(input + temp_offset)) == -1) {
881  return 0;
882  }
883 
884  temp_offset += sizeof(uint16_t);
885  //SCLogDebug("DecodeCIPRequestMSP number of services %d",num_services);
886 
887  for (int svc = 1; svc < num_services + 1; svc++)
888  {
889  if (temp_offset >= (input_len - sizeof(uint16_t)))
890  {
891  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
892  return 0;
893  }
894 
895  uint16_t svc_offset; //read set of service offsets
896  if (ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
897  (const uint8_t *)(input + temp_offset)) == -1) {
898  return 0;
899  }
900  temp_offset += sizeof(uint16_t);
901  //SCLogDebug("parseCIPRequestMSP service %d offset %d",svc, svc_offset);
902 
903  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
904  }
905 
906  return ret;
907 }
908 
909 
910 
911 /**
912  * \brief Decode CIP Response MultiService Packet.
913  * @param input, input_len data stream
914  * @param enip_data stores data from Packet
915  * @param offset current point in the packet
916  * @return 1 Packet ok
917  * @return 0 Packet has errors
918  */
919 int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len,
920  ENIPTransaction *enip_data, uint16_t offset)
921 {
922  int ret = 1;
923 
924  if (offset >= (input_len - sizeof(uint16_t)))
925  {
926  SCLogDebug("DecodeCIPResponseMSPPDU: Parsing beyond payload length");
927  return 0;
928  }
929  //use temp_offset just to grab the service offset, don't want to use and push offset
930  uint16_t temp_offset = offset;
931  uint16_t num_services;
932  if (ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
933  (const uint8_t *)(input + temp_offset)) == -1) {
934  return 0;
935  }
936  temp_offset += sizeof(uint16_t);
937  //SCLogDebug("DecodeCIPResponseMSP number of services %d", num_services);
938 
939  for (int svc = 0; svc < num_services; svc++) {
940  if (temp_offset >= (input_len - sizeof(uint16_t)))
941  {
942  SCLogDebug("DecodeCIPResponseMSP: Parsing beyond payload length");
943  return 0;
944  }
945 
946  uint16_t svc_offset; //read set of service offsets
947  if (ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
948  (const uint8_t *)(input + temp_offset)) == -1) {
949  return 0;
950  }
951  temp_offset += sizeof(uint16_t);
952  //SCLogDebug("parseCIPResponseMSP service %d offset %d", svc, svc_offset);
953 
954  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
955  }
956 
957  return ret;
958 }
PATH_INSTANCE_16BIT
#define PATH_INSTANCE_16BIT
Definition: app-layer-enip-common.h:86
util-byte.h
SEND_RR_DATA
#define SEND_RR_DATA
Definition: app-layer-enip-common.h:36
ENIPEncapAddressItem_::length
uint16_t length
Definition: app-layer-enip-common.h:118
ENIPTransaction_::encap_data_header
ENIPEncapDataHdr encap_data_header
Definition: app-layer-enip-common.h:199
len
uint8_t len
Definition: app-layer-dnp3.h:2
CIPServiceEntry_::response
struct CIPServiceEntry_::@2::@5 response
LIST_SERVICES
#define LIST_SERVICES
Definition: app-layer-enip-common.h:31
detect-engine.h
SegmentEntry_
Definition: app-layer-enip-common.h:154
CIP_CREATE
#define CIP_CREATE
Definition: app-layer-enip-common.h:71
ByteExtractUint64
int ByteExtractUint64(uint64_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:122
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
CIPServiceEntry_::request
struct CIPServiceEntry_::@2::@4 request
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
ByteExtractUint16
int ByteExtractUint16(uint16_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:164
CIPRespHdr_
Definition: app-layer-enip-common.h:146
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
SEND_UNIT_DATA
#define SEND_UNIT_DATA
Definition: app-layer-enip-common.h:37
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
DecodeENIPPDU
int DecodeENIPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data)
Decode ENIP Encapsulation Header.
Definition: app-layer-enip-common.c:191
ENIPEncapHdr_::session
uint32_t session
Definition: app-layer-enip-common.h:96
CONNECTED_DATA_ITEM
#define CONNECTED_DATA_ITEM
Definition: app-layer-enip-common.h:44
ENIPEncapDataItem_::length
uint16_t length
Definition: app-layer-enip-common.h:129
ENIPEncapHdr_::option
uint32_t option
Definition: app-layer-enip-common.h:98
CIP_STOP
#define CIP_STOP
Definition: app-layer-enip-common.h:70
ENIPEncapDataItem_::sequence_count
uint16_t sequence_count
Definition: app-layer-enip-common.h:130
PATH_INSTANCE_8BIT
#define PATH_INSTANCE_8BIT
Definition: app-layer-enip-common.h:85
NOP
#define NOP
Definition: app-layer-enip-common.h:30
DecodeCIPPDU
int DecodeCIPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP packet.
Definition: app-layer-enip-common.c:412
REGISTER_SESSION
#define REGISTER_SESSION
Definition: app-layer-enip-common.h:34
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
LIST_INTERFACES
#define LIST_INTERFACES
Definition: app-layer-enip-common.h:33
LIST_IDENTITY
#define LIST_IDENTITY
Definition: app-layer-enip-common.h:32
CANCEL
#define CANCEL
Definition: app-layer-enip-common.h:39
ENIPEncapDataHdr_::item_count
uint16_t item_count
Definition: app-layer-enip-common.h:110
util-unittest.h
ENIPEncapHdr_::command
uint16_t command
Definition: app-layer-enip-common.h:99
AttributeEntry_
Definition: app-layer-enip-common.h:162
util-unittest-helper.h
CIPServiceEntry_
Definition: app-layer-enip-common.h:169
DecodeCommonPacketFormatPDU
int DecodeCommonPacketFormatPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode Common Packet Format.
Definition: app-layer-enip-common.c:292
CIPReqHdr_
Definition: app-layer-enip-common.h:137
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
MAX_CIP_SERVICE
#define MAX_CIP_SERVICE
Definition: app-layer-enip-common.h:59
CIP_RESERVED
#define CIP_RESERVED
Definition: app-layer-enip-common.h:64
ENIPEncapHdr_::context
uint64_t context
Definition: app-layer-enip-common.h:95
CIP_DELETE
#define CIP_DELETE
Definition: app-layer-enip-common.h:72
CONNECTION_BASED
#define CONNECTION_BASED
Definition: app-layer-enip-common.h:43
CIP_APPLY_ATTR
#define CIP_APPLY_ATTR
Definition: app-layer-enip-common.h:74
DecodeCIPResponseMSPPDU
int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Response MultiService Packet.
Definition: app-layer-enip-common.c:919
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
ByteExtractUint32
int ByteExtractUint32(uint32_t *res, int e, uint16_t len, const uint8_t *bytes)
Definition: util-byte.c:143
pkt-var.h
DecodeCIPResponsePDU
int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Response.
Definition: app-layer-enip-common.c:748
DecodeCIPRequestMSPPDU
int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Request Multi Service Packet.
Definition: app-layer-enip-common.c:867
CIPServiceEntry_::service
uint8_t service
Definition: app-layer-enip-common.h:170
ENIPTransaction_::encap_data_item
ENIPEncapDataItem encap_data_item
Definition: app-layer-enip-common.h:201
ENIPEncapAddressItem_::type
uint16_t type
Definition: app-layer-enip-common.h:117
util-profiling.h
SCReturn
#define SCReturn
Definition: util-debug.h:273
CIP_START
#define CIP_START
Definition: app-layer-enip-common.h:69
ENIPEncapAddressItem_::sequence_num
uint32_t sequence_num
Definition: app-layer-enip-common.h:120
PATH_CLASS_8BIT
#define PATH_CLASS_8BIT
Definition: app-layer-enip-common.h:83
ENIPEncapDataHdr_
Definition: app-layer-enip-common.h:107
INDICATE_STATUS
#define INDICATE_STATUS
Definition: app-layer-enip-common.h:38
ENIPTransaction_
Definition: app-layer-enip-common.h:192
CIP_RESET
#define CIP_RESET
Definition: app-layer-enip-common.h:68
CIP_MSP
#define CIP_MSP
Definition: app-layer-enip-common.h:73
BYTE_LITTLE_ENDIAN
#define BYTE_LITTLE_ENDIAN
Definition: util-byte.h:30
CIP_GET_STATUS
#define CIP_GET_STATUS
Definition: app-layer-enip-common.h:80
suricata-common.h
ENIPEncapDataHdr_::interface_handle
uint32_t interface_handle
Definition: app-layer-enip-common.h:108
PATH_ATTR_8BIT
#define PATH_ATTR_8BIT
Definition: app-layer-enip-common.h:87
ENIPEncapHdr_::status
uint32_t status
Definition: app-layer-enip-common.h:97
CIP_GET_ATTR_ALL
#define CIP_GET_ATTR_ALL
Definition: app-layer-enip-common.h:65
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
ENIPEncapDataHdr_::timeout
uint16_t timeout
Definition: app-layer-enip-common.h:109
CIP_KICK_TIMER
#define CIP_KICK_TIMER
Definition: app-layer-enip-common.h:77
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ENIPTransaction_::service_count
uint16_t service_count
Definition: app-layer-enip-common.h:196
detect-parse.h
app-layer-enip-common.h
CIP_SET_ATTR_LIST
#define CIP_SET_ATTR_LIST
Definition: app-layer-enip-common.h:67
ENIPEncapDataItem_::type
uint16_t type
Definition: app-layer-enip-common.h:128
ENIPEncapHdr_::length
uint16_t length
Definition: app-layer-enip-common.h:100
CIP_GET_ATTR_LIST
#define CIP_GET_ATTR_LIST
Definition: app-layer-enip-common.h:66
CIP_OPEN_CONNECTION
#define CIP_OPEN_CONNECTION
Definition: app-layer-enip-common.h:78
DecodeCIPRequestPDU
int DecodeCIPRequestPDU(const uint8_t *input, uint32_t input_len, ENIPTransaction *enip_data, uint16_t offset)
Decode CIP Request.
Definition: app-layer-enip-common.c:456
SEQUENCE_ADDR_ITEM
#define SEQUENCE_ADDR_ITEM
Definition: app-layer-enip-common.h:46
ENIPEncapAddressItem_::conn_id
uint32_t conn_id
Definition: app-layer-enip-common.h:119
ENIPTransaction_::header
ENIPEncapHdr header
Definition: app-layer-enip-common.h:198
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
CIPServiceEntry_::direction
uint8_t direction
Definition: app-layer-enip-common.h:171
UNCONNECTED_DATA_ITEM
#define UNCONNECTED_DATA_ITEM
Definition: app-layer-enip-common.h:45
CIP_CHANGE_START
#define CIP_CHANGE_START
Definition: app-layer-enip-common.h:79
UNREGISTER_SESSION
#define UNREGISTER_SESSION
Definition: app-layer-enip-common.h:35
AttributeEntry_::attribute
uint16_t attribute
Definition: app-layer-enip-common.h:163
PATH_CLASS_16BIT
#define PATH_CLASS_16BIT
Definition: app-layer-enip-common.h:84
ENIPTransaction_::encap_addr_item
ENIPEncapAddressItem encap_addr_item
Definition: app-layer-enip-common.h:200
DecodeCIPRequestPathPDU
int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len, CIPServiceEntry *node, uint16_t offset)
Decode CIP Request Path.
Definition: app-layer-enip-common.c:575