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 /**
570  * \brief Deocde CIP Request Path
571  * @param input, input_len data stream
572  * @param enip_data stores data from Packet
573  * @param offset current point in the packet
574  * @param cipserviced the cip service rule
575  * @return 1 Packet matches
576  * @return 0 Packet not match
577  */
578 int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len,
579  CIPServiceEntry *node, uint16_t offset)
580 {
581  //SCLogDebug("DecodeCIPRequestPath: service 0x%x size %d length %d",
582  // node->service, node->request.path_size, input_len);
583 
584  if (node->request.path_size < 1)
585  {
586  //SCLogDebug("DecodeCIPRequestPath: empty path or CIP Response");
587  return 0;
588  }
589 
590  int bytes_remain = node->request.path_size;
591 
592  uint8_t reserved; //unused byte reserved by ODVA
593 
594  //8 bit fields
595  uint8_t req_path_instance8;
596  uint8_t req_path_attr8;
597 
598  //16 bit fields
599  uint16_t req_path_class16;
600  uint16_t req_path_instance16;
601 
602  uint16_t class = 0;
603 
604  SegmentEntry *seg = NULL;
605 
606  while (bytes_remain > 0)
607  {
608  uint8_t segment = 0;
609  if (ENIPExtractUint8(&segment, input, &offset, input_len) != 1)
610  {
611  return 0;
612  }
613  switch (segment)
614  { //assume order is class then instance. Can have multiple
615  case PATH_CLASS_8BIT: {
616  uint8_t req_path_class8 = 0;
617  if (ENIPExtractUint8(&req_path_class8, input, &offset, input_len) != 1) {
618  return 0;
619  }
620  class = (uint16_t) req_path_class8;
621  SCLogDebug("DecodeCIPRequestPathPDU: 8bit class 0x%x", class);
622 
623  seg = SCMalloc(sizeof(SegmentEntry));
624  if (unlikely(seg == NULL))
625  return 0;
626  seg->segment = segment;
627  seg->value = class;
628  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
629 
630  bytes_remain--;
631  break;
632  }
633  case PATH_INSTANCE_8BIT:
634  if (ENIPExtractUint8(&req_path_instance8, input, &offset, input_len) != 1)
635  {
636  return 0;
637  }
638  //skip instance, don't need to store
639  bytes_remain--;
640  break;
641  case PATH_ATTR_8BIT: //single attribute
642  if (ENIPExtractUint8(&req_path_attr8, input, &offset, input_len) != 1)
643  {
644  return 0;
645  }
646  //uint16_t attrib = (uint16_t) req_path_attr8;
647  //SCLogDebug("DecodeCIPRequestPath: 8bit attr 0x%x", attrib);
648 
649  seg = SCMalloc(sizeof(SegmentEntry));
650  if (unlikely(seg == NULL))
651  return 0;
652  seg->segment = segment;
653  seg->value = class;
654  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
655 
656  bytes_remain--;
657  break;
658  case PATH_CLASS_16BIT:
659  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) //skip reserved
660  {
661  return 0;
662  }
663  if (ENIPExtractUint16(&req_path_class16, input, &offset, input_len) != 1)
664  {
665  return 0;
666  }
667  class = req_path_class16;
668  SCLogDebug("DecodeCIPRequestPath: 16bit class 0x%x", class);
669 
670  seg = SCMalloc(sizeof(SegmentEntry));
671  if (unlikely(seg == NULL))
672  return 0;
673  seg->segment = segment;
674  seg->value = class;
675  TAILQ_INSERT_TAIL(&node->segment_list, seg, next);
676  if (bytes_remain >= 2)
677  {
678  bytes_remain = bytes_remain - 2;
679  } else
680  {
681  bytes_remain = 0;
682  }
683  break;
684  case PATH_INSTANCE_16BIT:
685  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1) // skip reserved
686  {
687  return 0;
688  }
689  if (ENIPExtractUint16(&req_path_instance16, input, &offset, input_len) != 1)
690  {
691  return 0;
692  }
693  //skip instance, don't need to store
694  if (bytes_remain >= 2)
695  {
696  bytes_remain = bytes_remain - 2;
697  } else
698  {
699  bytes_remain = 0;
700  }
701  break;
702  default:
703  SCLogDebug(
704  "DecodeCIPRequestPath: UNKNOWN SEGMENT 0x%x service 0x%x",
705  segment, node->service);
706  return 0;
707  }
708  }
709 
710  if ((node->service == CIP_SET_ATTR_LIST) || (node->service
711  == CIP_GET_ATTR_LIST))
712  {
713  uint16_t attr_list_count;
714  uint16_t attribute;
715  //parse get/set attribute list
716 
717  if (ENIPExtractUint16(&attr_list_count, input, &offset, input_len) != 1)
718  {
719  return 0;
720  }
721  SCLogDebug("DecodeCIPRequestPathPDU: attribute list count %d",
722  attr_list_count);
723  for (int i = 0; i < attr_list_count; i++)
724  {
725  if (ENIPExtractUint16(&attribute, input, &offset, input_len) != 1)
726  {
727  return 0;
728  }
729  SCLogDebug("DecodeCIPRequestPathPDU: attribute %d", attribute);
730  //save attrs
731  AttributeEntry *attr = SCMalloc(sizeof(AttributeEntry));
732  if (unlikely(attr == NULL))
733  return 0;
734  attr->attribute = attribute;
735  TAILQ_INSERT_TAIL(&node->attrib_list, attr, next);
736 
737  }
738  }
739 
740  return 1;
741 }
742 
743 /**
744  * \brief Decode CIP Response
745  * @param input, input_len data stream
746  * @param enip_data stores data from Packet
747  * @param offset current point in the packet
748  * @return 1 Packet ok
749  * @return 0 Packet has errors
750  */
751 int DecodeCIPResponsePDU(const uint8_t *input, uint32_t input_len,
752  ENIPTransaction *enip_data, uint16_t offset)
753 {
754  int ret = 1;
755 
756  if (enip_data->encap_data_item.length < sizeof(CIPRespHdr))
757  {
758  SCLogDebug("DecodeCIPResponse - Malformed CIP Data");
759  return 0;
760  }
761 
762  uint8_t service = 0; //<----CIP SERVICE
763  uint8_t reserved; //unused byte reserved by ODVA
764  uint16_t status;
765 
766  if (ENIPExtractUint8(&service, input, &offset, input_len) != 1)
767  {
768  return 0;
769  }
770  if (ENIPExtractUint8(&reserved, input, &offset, input_len) != 1)
771  {
772  return 0;
773  }
774  if (ENIPExtractUint16(&status, input, &offset, input_len) != 1)
775  {
776  return 0;
777  }
778 
779  //SCLogDebug("DecodeCIPResponse: service 0x%x",service);
780  service &= 0x7f; //strip off top bit to get service code. Responses have first bit as 1
781 
782  SCLogDebug("CIP service 0x%x status 0x%x", service, status);
783 
784  //reached maximum number of services
785  if (enip_data->service_count > 32)
786  {
787  SCLogDebug("DecodeCIPRequest: Maximum services reached");
788  return 0;
789  }
790 
791  //save CIP data
792  CIPServiceEntry *node = CIPServiceAlloc(enip_data);
793  if (node == NULL)
794  {
795  SCLogDebug("DecodeCIPRequest: Unable to create CIP service");
796  return 0;
797  }
798  node->direction = 1;
799  node->service = service;
800  node->response.status = status;
801 
802  SCLogDebug("DecodeCIPResponsePDU: service 0x%x size %d", node->service,
803  node->request.path_size);
804 
805  //list of CIP services is large and can be vendor specific, store CIP service anyways and let the rule decide the action
806  switch (service)
807  {
808  case CIP_RESERVED:
809  SCLogDebug("DecodeCIPResponse - CIP_RESERVED");
810  break;
811  case CIP_GET_ATTR_ALL:
812  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_ALL");
813  break;
814  case CIP_GET_ATTR_LIST:
815  SCLogDebug("DecodeCIPResponse - CIP_GET_ATTR_LIST");
816  break;
817  case CIP_SET_ATTR_LIST:
818  SCLogDebug("DecodeCIPResponse - CIP_SET_ATTR_LIST");
819  break;
820  case CIP_RESET:
821  SCLogDebug("DecodeCIPResponse - CIP_RESET");
822  break;
823  case CIP_START:
824  SCLogDebug("DecodeCIPResponse - CIP_START");
825  break;
826  case CIP_STOP:
827  SCLogDebug("DecodeCIPResponse - CIP_STOP");
828  break;
829  case CIP_CREATE:
830  SCLogDebug("DecodeCIPResponse - CIP_CREATE");
831  break;
832  case CIP_DELETE:
833  SCLogDebug("DecodeCIPResponse - CIP_DELETE");
834  break;
835  case CIP_MSP:
836  SCLogDebug("DecodeCIPResponse - CIP_MSP");
837  DecodeCIPResponseMSPPDU(input, input_len, enip_data, offset);
838  break;
839  case CIP_APPLY_ATTR:
840  SCLogDebug("DecodeCIPResponse - CIP_APPLY_ATTR");
841  break;
842  case CIP_KICK_TIMER:
843  SCLogDebug("DecodeCIPResponse - CIP_KICK_TIMER");
844  break;
845  case CIP_OPEN_CONNECTION:
846  SCLogDebug("DecodeCIPResponse - CIP_OPEN_CONNECTION");
847  break;
848  case CIP_CHANGE_START:
849  SCLogDebug("DecodeCIPResponse - CIP_CHANGE_START");
850  break;
851  case CIP_GET_STATUS:
852  SCLogDebug("DecodeCIPResponse - CIP_GET_STATUS");
853  break;
854  default:
855  SCLogDebug("DecodeCIPResponse - CIP SERVICE 0x%x", service);
856  }
857 
858  return ret;
859 }
860 
861 
862 /**
863  * \brief Decode CIP Request Multi Service Packet
864  * @param input, input_len data stream
865  * @param enip_data stores data from Packet
866  * @param offset current point in the packet
867  * @return 1 Packet ok
868  * @return 0 Packet has errors
869  */
870 int DecodeCIPRequestMSPPDU(const uint8_t *input, uint32_t input_len,
871  ENIPTransaction *enip_data, uint16_t offset)
872 {
873  int ret = 1;
874  if (offset >= (input_len - sizeof(uint16_t)))
875  {
876  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
877  return 0;
878  }
879  //use temp_offset just to grab the service offset, don't want to use and push offset
880  uint16_t temp_offset = offset;
881  uint16_t num_services;
882  if (ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
883  (const uint8_t *)(input + temp_offset)) == -1) {
884  return 0;
885  }
886 
887  temp_offset += sizeof(uint16_t);
888  //SCLogDebug("DecodeCIPRequestMSP number of services %d",num_services);
889 
890  for (int svc = 1; svc < num_services + 1; svc++)
891  {
892  if (temp_offset >= (input_len - sizeof(uint16_t)))
893  {
894  SCLogDebug("DecodeCIPRequestMSPPDU: Parsing beyond payload length");
895  return 0;
896  }
897 
898  uint16_t svc_offset; //read set of service offsets
899  if (ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
900  (const uint8_t *)(input + temp_offset)) == -1) {
901  return 0;
902  }
903  temp_offset += sizeof(uint16_t);
904  //SCLogDebug("parseCIPRequestMSP service %d offset %d",svc, svc_offset);
905 
906  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
907  }
908 
909  return ret;
910 }
911 
912 
913 
914 /**
915  * \brief Decode CIP Response MultiService Packet.
916  * @param input, input_len data stream
917  * @param enip_data stores data from Packet
918  * @param offset current point in the packet
919  * @return 1 Packet ok
920  * @return 0 Packet has errors
921  */
922 int DecodeCIPResponseMSPPDU(const uint8_t *input, uint32_t input_len,
923  ENIPTransaction *enip_data, uint16_t offset)
924 {
925  int ret = 1;
926 
927  if (offset >= (input_len - sizeof(uint16_t)))
928  {
929  SCLogDebug("DecodeCIPResponseMSPPDU: Parsing beyond payload length");
930  return 0;
931  }
932  //use temp_offset just to grab the service offset, don't want to use and push offset
933  uint16_t temp_offset = offset;
934  uint16_t num_services;
935  if (ByteExtractUint16(&num_services, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
936  (const uint8_t *)(input + temp_offset)) == -1) {
937  return 0;
938  }
939  temp_offset += sizeof(uint16_t);
940  //SCLogDebug("DecodeCIPResponseMSP number of services %d", num_services);
941 
942  for (int svc = 0; svc < num_services; svc++) {
943  if (temp_offset >= (input_len - sizeof(uint16_t)))
944  {
945  SCLogDebug("DecodeCIPResponseMSP: Parsing beyond payload length");
946  return 0;
947  }
948 
949  uint16_t svc_offset; //read set of service offsets
950  if (ByteExtractUint16(&svc_offset, BYTE_LITTLE_ENDIAN, sizeof(uint16_t),
951  (const uint8_t *)(input + temp_offset)) == -1) {
952  return 0;
953  }
954  temp_offset += sizeof(uint16_t);
955  //SCLogDebug("parseCIPResponseMSP service %d offset %d", svc, svc_offset);
956 
957  DecodeCIPPDU(input, input_len, enip_data, offset + svc_offset); //parse CIP at found offset
958  }
959 
960  return ret;
961 }
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
ENIPTransaction_::encap_data_header
ENIPEncapDataHdr encap_data_header
Definition: app-layer-enip-common.h:200
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:155
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:147
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
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:130
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:131
PATH_INSTANCE_8BIT
#define PATH_INSTANCE_8BIT
Definition: app-layer-enip-common.h:85
ENIPTransaction_::encap_addr_item
ENIPEncapAddresItem encap_addr_item
Definition: app-layer-enip-common.h:201
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:163
util-unittest-helper.h
CIPServiceEntry_
Definition: app-layer-enip-common.h:170
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:138
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
ENIPEncapAddresItem_::length
uint16_t length
Definition: app-layer-enip-common.h:119
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:922
SCEnter
#define SCEnter(...)
Definition: util-debug.h:298
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:751
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:870
CIPServiceEntry_::service
uint8_t service
Definition: app-layer-enip-common.h:171
ENIPTransaction_::encap_data_item
ENIPEncapDataItem encap_data_item
Definition: app-layer-enip-common.h:202
util-profiling.h
SCReturn
#define SCReturn
Definition: util-debug.h:300
CIP_START
#define CIP_START
Definition: app-layer-enip-common.h:69
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:193
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:197
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:129
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
ENIPEncapAddresItem_::sequence_num
uint32_t sequence_num
Definition: app-layer-enip-common.h:121
ENIPTransaction_::header
ENIPEncapHdr header
Definition: app-layer-enip-common.h:199
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
ENIPEncapAddresItem_::type
uint16_t type
Definition: app-layer-enip-common.h:118
ENIPEncapAddresItem_::conn_id
uint32_t conn_id
Definition: app-layer-enip-common.h:120
CIPServiceEntry_::direction
uint8_t direction
Definition: app-layer-enip-common.h:172
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:164
PATH_CLASS_16BIT
#define PATH_CLASS_16BIT
Definition: app-layer-enip-common.h:84
DecodeCIPRequestPathPDU
int DecodeCIPRequestPathPDU(const uint8_t *input, uint32_t input_len, CIPServiceEntry *node, uint16_t offset)
Deocde CIP Request Path.
Definition: app-layer-enip-common.c:578