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