1 //--------------------------------------------------------------------------
2 // Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 
19 // iec104_parse_information_object_elements.cc author Jared Rittle <jared.rittle@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "iec104_parse_information_object_elements.h"
26 
27 #include <cmath>
28 
29 #include "detection/detection_engine.h"
30 #include "events/event_queue.h"
31 #include "protocols/packet.h"
32 
33 #include "iec104.h"
34 #include "iec104_decode.h"
35 #include "iec104_module.h"
36 
37 using namespace snort;
38 
39 //
40 // Information Object Structures Parsing
41 //
42 //   This section contains functions to handle parsing and printing of the
43 //   various Information Object structures that make up the ASDU contents
44 //
45 
46 // COI: Cause of Initialization Structure
parseIec104Coi(const Iec104CoiType * coi)47 void parseIec104Coi(const Iec104CoiType* coi)
48 {
49     // throw an alert when the cause is in the reserved ranges (3-127)
50     if (coi->ui >= IEC104_COI_UI_RES3)
51     {
52         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_COI);
53     }
54 }
55 
56 // QOI: Qualifier of Interrogation Structure
parseIec104Qoi(const Iec104QoiType * qoi)57 void parseIec104Qoi(const Iec104QoiType* qoi)
58 {
59     // throw an alert when the cause is in the reserved ranges
60     if (qoi->qoi >= IEC104_QOI_RES1 and qoi->qoi <= IEC104_QOI_RES19)
61     {
62         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOI);
63     }
64     else if (qoi->qoi >= IEC104_QOI_RES37)
65     {
66         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOI);
67     }
68 }
69 
70 // QCC: Qualifier of Counter Interrogation Command Structure
parseIec104Qcc(const Iec104QccType * qcc)71 void parseIec104Qcc(const Iec104QccType* qcc)
72 {
73     // throw an alert when a reserved or invalid value is set
74     if (qcc->rqt >= IEC104_QCC_RQT_RES32)
75     {
76         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QCC);
77     }
78 }
79 
80 // QPM: Qualifier of Parameter of Measured Values Structure
parseIec104Qpm(const Iec104QpmType * qpm)81 void parseIec104Qpm(const Iec104QpmType* qpm)
82 {
83     // throw an alert when a reserved or invalid value is set
84     if (qpm->kpa >= IEC104_QPM_KPA_RES5)
85     {
86         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QPM_KPA);
87     }
88 
89     if (qpm->lpc)
90     {
91         DetectionEngine::queue_event(GID_IEC104, IEC104_ABNORMAL_QPM_LPC);
92     }
93 
94     if (qpm->pop)
95     {
96         DetectionEngine::queue_event(GID_IEC104, IEC104_ABNORMAL_QPM_POP);
97     }
98 }
99 
100 // QPA: Qualifier of Parameter Activation Structure
parseIec104Qpa(const Iec104QpaType * qpa)101 void parseIec104Qpa(const Iec104QpaType* qpa)
102 {
103     // throw an alert when a reserved or invalid value is set
104     if (qpa->qpa >= IEC104_QPA_RES4)
105     {
106         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QPA);
107     }
108 }
109 
110 // QOC: Qualifier of Command Structure
parseIec104Qoc(uint8_t qu,uint8_t se)111 void parseIec104Qoc(uint8_t qu, uint8_t se)
112 {
113     // throw an alert when a reserved or invalid value is set
114     if (qu >= IEC104_QOC_QU_RES4 and qu <= IEC104_QOC_QU_RES31)
115     {
116         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOC);
117     }
118 
119     if (se >= 2)
120     {
121         // error indicating that parsing couldn't finish
122     }
123 }
124 
125 // QRP: Qualifier of Reset Process Structure
parseIec104Qrp(const Iec104QrpType * qrp)126 void parseIec104Qrp(const Iec104QrpType* qrp)
127 {
128     // throw an alert when a reserved or invalid value is set
129     if (qrp->qrp >= IEC104_QRP_RES3)
130     {
131         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QRP);
132     }
133 }
134 
135 // FRQ: File Ready Qualifier Structure
parseIec104Frq(const Iec104FrqType * frq)136 void parseIec104Frq(const Iec104FrqType* frq)
137 {
138     // throw an alert when a reserved or invalid value is set
139     if (frq->ui >= IEC104_FRQ_UI_RES1)
140     {
141         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_FRQ);
142     }
143 }
144 
145 // SRQ: Section Ready Qualifier Structure
parseIec104Srq(const Iec104SrqType * srq)146 void parseIec104Srq(const Iec104SrqType* srq)
147 {
148     // throw an alert when a reserved or invalid value is set
149     if (srq->ui >= IEC104_SRQ_UI_RES1)
150     {
151         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SRQ);
152     }
153 }
154 
155 // SCQ: Select and Call Qualifier Structure
parseIec104Scq(const Iec104ScqType * scq)156 void parseIec104Scq(const Iec104ScqType* scq)
157 {
158     // throw an alert when a reserved or invalid value is set
159     if (scq->ui1 >= IEC104_SCQ_UI1_RES8)
160     {
161         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCQ);
162     }
163 
164     if (scq->ui2 >= IEC104_SCQ_UI2_RES6)
165     {
166         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCQ);
167     }
168 }
169 
170 // LSQ: Last Section or Segment Qualifier Structure
parseIec104Lsq(const Iec104LsqType * lsq)171 void parseIec104Lsq(const Iec104LsqType* lsq)
172 {
173     // throw an alert when a reserved or invalid value is set
174     if (lsq->lsq >= IEC104_LSQ_RES5)
175     {
176         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_LSQ);
177     }
178 }
179 
180 // AFQ: Acknowledge File or Section Qualifier Structure
parseIec104Afq(const Iec104AfqType * afq)181 void parseIec104Afq(const Iec104AfqType* afq)
182 {
183     // throw an alert when a reserved or invalid value is set
184     if (afq->ui1 >= IEC104_AFQ_UI1_RES5)
185     {
186         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_AFQ);
187     }
188     if (afq->ui2 >= IEC104_AFQ_UI2_RES6)
189     {
190         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_AFQ);
191     }
192 }
193 
parseIec104Vsq(const Iec104ApciI * apci)194 uint32_t parseIec104Vsq(const Iec104ApciI* apci)
195 {
196     // number of elements == 0 is caught in check apdu
197 
198     uint32_t informationObjectSubgroupSize = 0;
199 
200     // determine the size of the current message type group
201     switch(apci->asdu.typeId)
202     {
203         case IEC104_ASDU_M_SP_NA_1:
204         {
205             informationObjectSubgroupSize = sizeof(Iec104M_SP_NA_1_IO_Subgroup);
206             break;
207         }
208         case IEC104_ASDU_M_SP_TA_1:
209         {
210             informationObjectSubgroupSize = sizeof(Iec104M_SP_TA_1_IO_Subgroup);
211             break;
212         }
213         case IEC104_ASDU_M_DP_NA_1:
214         {
215             informationObjectSubgroupSize = sizeof(Iec104M_DP_NA_1_IO_Subgroup);
216             break;
217         }
218         case IEC104_ASDU_M_DP_TA_1:
219         {
220             informationObjectSubgroupSize = sizeof(Iec104M_DP_TA_1_IO_Subgroup);
221             break;
222         }
223         case IEC104_ASDU_M_ST_NA_1:
224         {
225             informationObjectSubgroupSize = sizeof(Iec104M_ST_NA_1_IO_Subgroup);
226             break;
227         }
228         case IEC104_ASDU_M_ST_TA_1:
229         {
230             informationObjectSubgroupSize = sizeof(Iec104M_ST_TA_1_IO_Subgroup);
231             break;
232         }
233         case IEC104_ASDU_M_BO_NA_1:
234         {
235             informationObjectSubgroupSize = sizeof(Iec104M_BO_NA_1_IO_Subgroup);
236             break;
237         }
238         case IEC104_ASDU_M_BO_TA_1:
239         {
240             informationObjectSubgroupSize = sizeof(Iec104M_BO_TA_1_IO_Subgroup);
241             break;
242         }
243         case IEC104_ASDU_M_ME_NA_1:
244         {
245             informationObjectSubgroupSize = sizeof(Iec104M_ME_NA_1_IO_Subgroup);
246             break;
247         }
248         case IEC104_ASDU_M_ME_TA_1:
249         {
250             informationObjectSubgroupSize = sizeof(Iec104M_ME_TA_1_IO_Subgroup);
251             break;
252         }
253         case IEC104_ASDU_M_ME_NB_1:
254         {
255             informationObjectSubgroupSize = sizeof(Iec104M_ME_NB_1_IO_Subgroup);
256             break;
257         }
258         case IEC104_ASDU_M_ME_TB_1:
259         {
260             informationObjectSubgroupSize = sizeof(Iec104M_ME_TB_1_IO_Subgroup);
261             break;
262         }
263         case IEC104_ASDU_M_ME_NC_1:
264         {
265             informationObjectSubgroupSize = sizeof(Iec104M_ME_NC_1_IO_Subgroup);
266             break;
267         }
268         case IEC104_ASDU_M_ME_TC_1:
269         {
270             informationObjectSubgroupSize = sizeof(Iec104M_ME_TC_1_IO_Subgroup);
271             break;
272         }
273         case IEC104_ASDU_M_IT_NA_1:
274         {
275             informationObjectSubgroupSize = sizeof(Iec104M_IT_NA_1_IO_Subgroup);
276             break;
277         }
278         case IEC104_ASDU_M_IT_TA_1:
279         {
280             informationObjectSubgroupSize = sizeof(Iec104M_IT_TA_1_IO_Subgroup);
281             break;
282         }
283         case IEC104_ASDU_M_EP_TA_1:
284         {
285             informationObjectSubgroupSize = sizeof(Iec104M_EP_TA_1_IO_Subgroup);
286             break;
287         }
288         case IEC104_ASDU_M_EP_TB_1:
289         {
290             informationObjectSubgroupSize = sizeof(Iec104M_EP_TB_1_IO_Subgroup);
291             break;
292         }
293         case IEC104_ASDU_M_EP_TC_1:
294         {
295             informationObjectSubgroupSize = sizeof(Iec104M_EP_TC_1_IO_Subgroup);
296             break;
297         }
298         case IEC104_ASDU_M_PS_NA_1:
299         {
300             informationObjectSubgroupSize = sizeof(Iec104M_PS_NA_1_IO_Subgroup);
301             break;
302         }
303         case IEC104_ASDU_M_ME_ND_1:
304         {
305             informationObjectSubgroupSize = sizeof(Iec104M_ME_ND_1_IO_Subgroup);
306             break;
307         }
308         case IEC104_ASDU_M_SP_TB_1:
309         {
310             informationObjectSubgroupSize = sizeof(Iec104M_SP_TB_1_IO_Subgroup);
311             break;
312         }
313         case IEC104_ASDU_M_DP_TB_1:
314         {
315             informationObjectSubgroupSize = sizeof(Iec104M_DP_TB_1_IO_Subgroup);
316             break;
317         }
318         case IEC104_ASDU_M_ST_TB_1:
319         {
320             informationObjectSubgroupSize = sizeof(Iec104M_ST_TB_1_IO_Subgroup);
321             break;
322         }
323         case IEC104_ASDU_M_BO_TB_1:
324         {
325             informationObjectSubgroupSize = sizeof(Iec104M_BO_TB_1_IO_Subgroup);
326             break;
327         }
328         case IEC104_ASDU_M_ME_TD_1:
329         {
330             informationObjectSubgroupSize = sizeof(Iec104M_ME_TD_1_IO_Subgroup);
331             break;
332         }
333         case IEC104_ASDU_M_ME_TE_1:
334         {
335             informationObjectSubgroupSize = sizeof(Iec104M_ME_TE_1_IO_Subgroup);
336             break;
337         }
338         case IEC104_ASDU_M_ME_TF_1:
339         {
340             informationObjectSubgroupSize = sizeof(Iec104M_ME_TF_1_IO_Subgroup);
341             break;
342         }
343         case IEC104_ASDU_M_IT_TB_1:
344         {
345             informationObjectSubgroupSize = sizeof(Iec104M_IT_TB_1_IO_Subgroup);
346             break;
347         }
348         case IEC104_ASDU_M_EP_TD_1:
349         {
350             informationObjectSubgroupSize = sizeof(Iec104M_EP_TD_1_IO_Subgroup);
351             break;
352         }
353         case IEC104_ASDU_M_EP_TE_1:
354         {
355             informationObjectSubgroupSize = sizeof(Iec104M_EP_TE_1_IO_Subgroup);
356             break;
357         }
358         case IEC104_ASDU_M_EP_TF_1:
359         {
360             informationObjectSubgroupSize = sizeof(Iec104M_EP_TF_1_IO_Subgroup);
361             break;
362         }
363         case IEC104_ASDU_C_SC_NA_1:
364         {
365             informationObjectSubgroupSize = sizeof(Iec104C_SC_NA_1_IO_Subgroup);
366             break;
367         }
368         case IEC104_ASDU_C_DC_NA_1:
369         {
370             informationObjectSubgroupSize = sizeof(Iec104C_DC_NA_1_IO_Subgroup);
371             break;
372         }
373         case IEC104_ASDU_C_RC_NA_1:
374         {
375             informationObjectSubgroupSize = sizeof(Iec104C_RC_NA_1_IO_Subgroup);
376             break;
377         }
378         case IEC104_ASDU_C_SE_NA_1:
379         {
380             informationObjectSubgroupSize = sizeof(Iec104C_SE_NA_1_IO_Subgroup);
381             break;
382         }
383         case IEC104_ASDU_C_SE_NB_1:
384         {
385             informationObjectSubgroupSize = sizeof(Iec104C_SE_NB_1_IO_Subgroup);
386             break;
387         }
388         case IEC104_ASDU_C_SE_NC_1:
389         {
390             informationObjectSubgroupSize = sizeof(Iec104C_SE_NC_1_IO_Subgroup);
391             break;
392         }
393         case IEC104_ASDU_C_BO_NA_1:
394         {
395             informationObjectSubgroupSize = sizeof(Iec104C_BO_NA_1_IO_Subgroup);
396             break;
397         }
398         case IEC104_ASDU_C_SC_TA_1:
399         {
400             informationObjectSubgroupSize = sizeof(Iec104C_SC_TA_1_IO_Subgroup);
401             break;
402         }
403         case IEC104_ASDU_C_DC_TA_1:
404         {
405             informationObjectSubgroupSize = sizeof(Iec104C_DC_TA_1_IO_Subgroup);
406             break;
407         }
408         case IEC104_ASDU_C_RC_TA_1:
409         {
410             informationObjectSubgroupSize = sizeof(Iec104C_RC_TA_1_IO_Subgroup);
411             break;
412         }
413         case IEC104_ASDU_C_SE_TA_1:
414         {
415             informationObjectSubgroupSize = sizeof(Iec104C_SE_TA_1_IO_Subgroup);
416             break;
417         }
418         case IEC104_ASDU_C_SE_TB_1:
419         {
420             informationObjectSubgroupSize = sizeof(Iec104C_SE_TB_1_IO_Subgroup);
421             break;
422         }
423         case IEC104_ASDU_C_SE_TC_1:
424         {
425             informationObjectSubgroupSize = sizeof(Iec104C_SE_TC_1_IO_Subgroup);
426             break;
427         }
428         case IEC104_ASDU_C_BO_TA_1:
429         {
430             informationObjectSubgroupSize = sizeof(Iec104C_BO_TA_1_IO_Subgroup);
431             break;
432         }
433         case IEC104_ASDU_M_EI_NA_1:
434         {
435             informationObjectSubgroupSize = sizeof(Iec104M_EI_NA_1_IO_Subgroup);
436             break;
437         }
438         case IEC104_ASDU_C_IC_NA_1:
439         {
440             informationObjectSubgroupSize = sizeof(Iec104C_IC_NA_1_IO_Subgroup);
441             break;
442         }
443         case IEC104_ASDU_C_CI_NA_1:
444         {
445             informationObjectSubgroupSize = sizeof(Iec104C_CI_NA_1_IO_Subgroup);
446             break;
447         }
448         case IEC104_ASDU_C_RD_NA_1:
449         {
450             informationObjectSubgroupSize = sizeof(Iec104C_RD_NA_1_IO_Subgroup);
451             break;
452         }
453         case IEC104_ASDU_C_CS_NA_1:
454         {
455             informationObjectSubgroupSize = sizeof(Iec104C_CS_NA_1_IO_Subgroup);
456             break;
457         }
458         case IEC104_ASDU_C_TS_NA_1:
459         {
460             informationObjectSubgroupSize = sizeof(Iec104C_TS_NA_1_IO_Subgroup);
461             break;
462         }
463         case IEC104_ASDU_C_RP_NA_1:
464         {
465             informationObjectSubgroupSize = sizeof(Iec104C_RP_NA_1_IO_Subgroup);
466             break;
467         }
468         case IEC104_ASDU_C_CD_NA_1:
469         {
470             informationObjectSubgroupSize = sizeof(Iec104C_CD_NA_1_IO_Subgroup);
471             break;
472         }
473         case IEC104_ASDU_C_TS_TA_1:
474         {
475             informationObjectSubgroupSize = sizeof(Iec104C_TS_TA_1_IO_Subgroup);
476             break;
477         }
478         case IEC104_ASDU_P_ME_NA_1:
479         {
480             informationObjectSubgroupSize = sizeof(Iec104P_ME_NA_1_IO_Subgroup);
481             break;
482         }
483         case IEC104_ASDU_P_ME_NB_1:
484         {
485             informationObjectSubgroupSize = sizeof(Iec104P_ME_NB_1_IO_Subgroup);
486             break;
487         }
488         case IEC104_ASDU_P_ME_NC_1:
489         {
490             informationObjectSubgroupSize = sizeof(Iec104P_ME_NC_1_IO_Subgroup);
491             break;
492         }
493         case IEC104_ASDU_P_AC_NA_1:
494         {
495             informationObjectSubgroupSize = sizeof(Iec104P_AC_NA_1_IO_Subgroup);
496             break;
497         }
498         case IEC104_ASDU_F_FR_NA_1:
499         {
500             informationObjectSubgroupSize = sizeof(Iec104F_FR_NA_1_IO_Subgroup);
501             break;
502         }
503         case IEC104_ASDU_F_SR_NA_1:
504         {
505             informationObjectSubgroupSize = sizeof(Iec104F_SR_NA_1_IO_Subgroup);
506             break;
507         }
508         case IEC104_ASDU_F_SC_NA_1:
509         {
510             informationObjectSubgroupSize = sizeof(Iec104F_SC_NA_1_IO_Subgroup);
511             break;
512         }
513         case IEC104_ASDU_F_LS_NA_1:
514         {
515             informationObjectSubgroupSize = sizeof(Iec104F_LS_NA_1_IO_Subgroup);
516             break;
517         }
518         case IEC104_ASDU_F_AF_NA_1:
519         {
520             informationObjectSubgroupSize = sizeof(Iec104F_AF_NA_1_IO_Subgroup);
521             break;
522         }
523         case IEC104_ASDU_F_SG_NA_1:
524         {
525             informationObjectSubgroupSize = sizeof(Iec104F_SG_NA_1_IO_Subgroup);
526             break;
527         }
528         case IEC104_ASDU_F_DR_TA_1:
529         {
530             informationObjectSubgroupSize = sizeof(Iec104F_DR_TA_1_IO_Subgroup);
531             break;
532         }
533         case IEC104_ASDU_F_SC_NB_1:
534         {
535             informationObjectSubgroupSize = sizeof(Iec104F_SC_NB_1_IO_Subgroup);
536             break;
537         }
538     }
539 
540     // make sure the reported number of elements would not exceed the packet size
541     // * take the apci->header.length value
542     // * subtract off type id, vsq, cause of tx, and 2-byte common address sizes
543     // * if the sq bit is set, subtract off the IOA
544     // * use a switch statement with cases of each message type to get the size of one group
545     // * divide the result of the earlier calculation by this group size to get the maximum allowable groups without overflowing
546     uint8_t maxNumberOfElements = 0;
547 
548     if (informationObjectSubgroupSize)
549     {
550         uint32_t reported_msg_len = apci->header.length;
551         if (reported_msg_len >= IEC104_APCI_TYPE_I_MIN_LEN) {
552             if (apci->asdu.variableStructureQualifier.sq == 0)
553             {
554                 uint32_t informationObjectGroupSize = informationObjectSubgroupSize + sizeof(const Iec104InformationObjectAddressThreeOctetType);
555                 maxNumberOfElements = (reported_msg_len
556                                        - sizeof(uint8_t)  // type id
557                                        - sizeof(const Iec104VariableStructureQualifierType)
558                                        - sizeof(const Iec104CauseOfTransmissionType)
559                                        - sizeof(const Iec104CommonAddressOfAsduType)
560                                        ) / informationObjectGroupSize;
561             }
562             else
563             {
564                 maxNumberOfElements = (reported_msg_len
565                                        - sizeof(uint8_t)  // type id
566                                        - sizeof(const Iec104VariableStructureQualifierType)
567                                        - sizeof(const Iec104CauseOfTransmissionType)
568                                        - sizeof(const Iec104CommonAddressOfAsduType)
569                                        - sizeof(const Iec104InformationObjectAddressThreeOctetType)
570                                        ) / informationObjectSubgroupSize;
571             }
572         }
573     }
574 
575     uint32_t verifiedNumberOfElements = apci->asdu.variableStructureQualifier.numberOfElements;
576     if (verifiedNumberOfElements > 0 and verifiedNumberOfElements <= maxNumberOfElements)
577     {
578         // do nothing
579     }
580     else
581     {
582         verifiedNumberOfElements = 0;
583         DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE);
584     }
585 
586     // if the SQ is set and the number of elements is only one something is off
587     // this case does not apply in cases where the SQ bit being set is the only option
588     // the only place this is known to exist is in F_DR_TA_1
589     if (apci->asdu.variableStructureQualifier.sq > 0
590         and apci->asdu.variableStructureQualifier.numberOfElements == 1
591         and apci->asdu.typeId != IEC104_ASDU_F_DR_TA_1)
592     {
593         DetectionEngine::queue_event(GID_IEC104, IEC104_VSQ_ABNORMAL_SQ);
594     }
595 
596 
597     return verifiedNumberOfElements;
598 }
599 
parseIec104CauseOfTx(const Iec104ApciI * apci)600 void parseIec104CauseOfTx(const Iec104ApciI* apci)
601 {
602     // no alerts are needed here as they are processed in checkIec104Asdu
603 
604     if (!apci)
605     {
606         // error indicating that parsing couldn't finish
607     }
608 }
609 
parseIec104TwoOctetCommonAddress(const Iec104ApciI * apci)610 void parseIec104TwoOctetCommonAddress(const Iec104ApciI* apci)
611 {
612     // provide an alert if a null common address is provided
613     if (apci->asdu.commonAddressOfAsdu.commonAddress == 0)
614     {
615         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_COMMON_ADDRESS);
616     }
617 }
618 
parseIec104InformationObjectAddressWithThreeOctets(const Iec104InformationObjectAddressThreeOctetType * ioa)619 void parseIec104InformationObjectAddressWithThreeOctets(
620     const Iec104InformationObjectAddressThreeOctetType* ioa)
621 {
622     // Nothing worth alerting on here
623 
624     if (!ioa)
625     {
626         // error indicating that parsing couldn't finish
627     }
628 }
629 
630 // SIQ: Single Point Information with Quality Descriptor Structure
parseIec104Siq(const Iec104SiqType * siq)631 void parseIec104Siq(const Iec104SiqType* siq)
632 {
633     // provide an alert if the reserved field is used
634     if (siq->reserved)
635     {
636         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SIQ);
637     }
638 }
639 
640 // DIQ: Double Point Information with Quality Descriptor Structure
parseIec104Diq(const Iec104DiqType * diq)641 void parseIec104Diq(const Iec104DiqType* diq)
642 {
643     // provide an alert if the reserved field is used
644     if (diq->reserved)
645     {
646         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_DIQ);
647     }
648 }
649 
650 // QDS: Quality Descriptor Structure
parseIec104Qds(const Iec104QdsType * qds)651 void parseIec104Qds(const Iec104QdsType* qds)
652 {
653     // provide an alert if the reserved field is used
654     if (qds->reserved)
655     {
656         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QDS);
657     }
658 }
659 
660 // QDP: Quality Descriptor for Events of Protection Equipment Structure
parseIec104Qdp(const Iec104QdpType * qdp)661 void parseIec104Qdp(const Iec104QdpType* qdp)
662 {
663     // provide an alert if the reserved field is used
664     if (qdp->reserved)
665     {
666         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QDP);
667     }
668 }
669 
670 // VTI: Value with Transient State Indication Structure
parseIec104Vti(const Iec104VtiType * vti)671 void parseIec104Vti(const Iec104VtiType* vti)
672 {
673     // Nothing worth alerting on here
674 
675     if (!vti)
676     {
677         // error indicating that parsing couldn't finish
678     }
679 }
680 
681 // NVA: Normalized Value Structure
parseIec104Nva(const Iec104NvaType * nva)682 void parseIec104Nva(const Iec104NvaType* nva)
683 {
684     // Nothing worth alerting on here
685 
686     if (!nva)
687     {
688         // error indicating that parsing couldn't finish
689     }
690 }
691 
692 // SVA: Scaled Value Structure
parseIec104Sva(const Iec104SvaType * sva)693 void parseIec104Sva(const Iec104SvaType* sva)
694 {
695     // Nothing worth alerting on here
696 
697     if (!sva)
698     {
699         // error indicating that parsing couldn't finish
700     }
701 }
702 
703 // IEEE_STD_754: Short Floating Point Number Structure
parseIec104IeeeStd754(const Iec104IeeeStd754Type * ieeeStd754)704 void parseIec104IeeeStd754(const Iec104IeeeStd754Type* ieeeStd754)
705 {
706     //FIXIT-E: keep investigating possible alerts here
707 
708     // convert the passed IEEE Std 754 value to big endian
709     uint32_t fixedIeeeStd754 = htonl(ieeeStd754->ieeeStd754);
710 
711     // break out individual fields for calculation
712     // f == fraction, e == exponent, s == sign
713     // +-----------------------------------------------------------------+
714     // | 1                               0                               |
715     // | f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 |
716     // +-----------------------------------------------------------------+
717     // | s e e e e e e e e f f f f f f f f f f f f f f f f f f f f f f f |
718     // +-----------------------------------------------------------------+
719     uint32_t ieeeStd754RawFraction = fixedIeeeStd754 & 0x007FFFFF;
720     uint8_t ieeeStd754RawExponent = (fixedIeeeStd754 >> 0x17) & 0xFF;
721 
722     // true exponent cannot be above 127 (raw 0xff)
723     if (ieeeStd754RawExponent == 0xFF)
724     {
725         // alert on infinity if raw exponent == 0xff and fraction == 0x00
726         if (ieeeStd754RawFraction == 0)
727         {
728             DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_IEEE_STD_754_INFINITY);
729         }
730         // alert on NaN if raw exponent == 0xff and fraction > 0x00
731         else
732         {
733             DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_IEEE_STD_754_NAN);
734         }
735     }
736 }
737 
738 // BCR: Binary Counter Reading Structure
parseIec104Bcr(const Iec104BcrType * bcr)739 void parseIec104Bcr(const Iec104BcrType* bcr)
740 {
741     // Nothing worth alerting on here
742 
743     if (!bcr)
744     {
745         // error indicating that parsing couldn't finish
746     }
747 }
748 
749 // SEP: Single Event of Protection Equipment Structure
parseIec104Sep(const Iec104SepType * sep)750 void parseIec104Sep(const Iec104SepType* sep)
751 {
752     // provide an alert if the reserved field is used
753     if (sep->reserved)
754     {
755         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SEP);
756     }
757 }
758 
759 // SPE: Start Event of Protection Equipment Structure
parseIec104Spe(const Iec104SpeType * spe)760 void parseIec104Spe(const Iec104SpeType* spe)
761 {
762     // provide an alert if the reserved field is used
763     if (spe->reserved)
764     {
765         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SPE);
766     }
767 }
768 
769 // OCI: Output Circuit Information Structure
parseIec104Oci(const Iec104OciType * oci)770 void parseIec104Oci(const Iec104OciType* oci)
771 {
772     // provide an alert if the reserved field is used
773     if (oci->reserved)
774     {
775         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_OCI);
776     }
777 }
778 
779 // BSI: Binary State Information Structure
parseIec104Bsi(const Iec104BsiType * bsi)780 void parseIec104Bsi(const Iec104BsiType* bsi)
781 {
782     // Nothing worth alerting on here
783 
784     if (!bsi)
785     {
786         // error indicating that parsing couldn't finish
787     }
788 }
789 
790 // FBP: Fixed Test Bit Pattern Structure
parseIec104Fbp(const Iec104FbpType * fbp)791 void parseIec104Fbp(const Iec104FbpType* fbp)
792 {
793     // provide an alert if the FBP is not \x55\xAA
794     if (fbp->fixedTestBitPattern != 0x55AA)
795     {
796         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_FBP);
797     }
798 }
799 
800 // SCO: Single Command Structure
parseIec104Sco(const Iec104ScoType * sco)801 void parseIec104Sco(const Iec104ScoType* sco)
802 {
803     // provide an alert if the reserved field is used
804     if (sco->reserved)
805     {
806         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCO);
807     }
808 
809     // parse the Qualifier of Command structure
810     parseIec104Qoc(sco->qu, sco->se);
811 }
812 
813 // DCO: Double Command Structure
parseIec104Dco(const Iec104DcoType * dco)814 void parseIec104Dco(const Iec104DcoType* dco)
815 {
816     // throw an alert when one of the defined invalid command states are detected
817     if (dco->dcs == IEC104_DCO_DCS_NOTPERMITTED1 or dco->dcs == IEC104_DCO_DCS_NOTPERMITTED2)
818     {
819         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DCO);
820     }
821 
822     // parse the Qualifier of Command structure
823     parseIec104Qoc(dco->qu, dco->se);
824 }
825 
826 // RCO: Regulating Step Command Structure
parseIec104Rco(const Iec104RcoType * rco)827 void parseIec104Rco(const Iec104RcoType* rco)
828 {
829     // throw an alert when one of the defined invalid command states are detected
830     if (rco->rcs == IEC104_RCO_RCS_NOTPERMITTED1 or rco->rcs == IEC104_RCO_RCS_NOTPERMITTED2)
831     {
832         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_RCO);
833     }
834 
835     // parse the Qualifier of Command structure
836     parseIec104Qoc(rco->qu, rco->se);
837 }
838 
839 // Time2a Milliseconds Structure
parseIec104Time2aMilliseconds(const Iec104Time2aMillisecondsType * time2aMilliseconds)840 void parseIec104Time2aMilliseconds(const Iec104Time2aMillisecondsType* time2aMilliseconds)
841 {
842     // ensure milliseconds aren't over the maximum allowed value
843     if (time2aMilliseconds->milliseconds >= IEC104_MS_IN_MINUTE)
844     {
845         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MS_IN_MINUTE);
846     }
847 }
848 
849 // Time2a IVResMinute Structure
parseIec104Time2aIvresminute(const Iec104Time2aIvresminuteType * time2aIvresminute)850 void parseIec104Time2aIvresminute(const Iec104Time2aIvresminuteType* time2aIvresminute)
851 {
852     // ensure minutes arent over 59
853     if (time2aIvresminute->minutes >= IEC104_MINS_IN_HOUR)
854     {
855         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MINS_IN_HOUR);
856     }
857 
858     // provide an alert if the reserved field is used
859     if (time2aIvresminute->res)
860     {
861         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_MINS_IN_HOUR);
862     }
863 }
864 
865 // Time2a SURes2Hour Structure
parseIec104Time2aSures2hour(const Iec104Time2aSures2hourType * time2aSures2hour)866 void parseIec104Time2aSures2hour(const Iec104Time2aSures2hourType* time2aSures2hour)
867 {
868     // ensure hours arent over 23
869     if (time2aSures2hour->hours >= IEC104_HOURS_IN_DAY)
870     {
871         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_HOURS_IN_DAY);
872     }
873 
874     // provide an alert if the reserved field is used
875     if (time2aSures2hour->res2)
876     {
877         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_HOURS_IN_DAY);
878     }
879 }
880 
isLeapYear(uint32_t yearOffset)881 static bool isLeapYear(uint32_t yearOffset)
882 {
883     // need to make sure we use the real year value and not just the offset
884     uint32_t trueYear = IEC104_TIME2ARES4YEAR_BASE + yearOffset;
885 
886     // determine if the current year matches the following criteria:
887     // (ref: https://docs.microsoft.com/en-us/office/troubleshoot/excel/determine-a-leap-year)
888     //    1. If the year is evenly divisible by 4, go to step 2. Otherwise, go to step 5.
889     //    2. If the year is evenly divisible by 100, go to step 3. Otherwise, go to step 4.
890     //    3. If the year is evenly divisible by 400, go to step 4. Otherwise, go to step 5.
891     //    4. The year is a leap year (it has 366 days).
892     //    5. The year is not a leap year (it has 365 days).
893 
894     if (trueYear % 4 == 0)
895     {
896         if (trueYear % 100 == 0)
897         {
898             if (trueYear % 400 == 0)
899             {
900                 // year is evenly divisible by 4, 100, and 400
901                 // leap year
902                 return true;
903             }
904             else
905             {
906                 // year is evenly divisible by 4, and 100 but NOT 400
907                 // NOT a leap year
908                 return false;
909             }
910         }
911         else
912         {
913             // year is evenly divisible by 4 but not evenly divisible by 100
914             // leap year
915             return true;
916         }
917     }
918     else
919     {
920         // year is not evenly divisible by 4
921         // NOT a leap year
922         return false;
923     }
924 }
925 
926 // Time2a DOWDay Structure
parseIec104Time2aDowday(const Iec104Cp56Time2aType * sevenOctetBinaryTime)927 void parseIec104Time2aDowday(const Iec104Cp56Time2aType* sevenOctetBinaryTime)
928 {
929     // Day of week will always be between 0 and 7 since the field is only 3 bits
930     // make sure month is at least 1 and no more than 12
931     if (sevenOctetBinaryTime->res3month.month >= IEC104_MONTH_JAN
932         and sevenOctetBinaryTime->res3month.month <= IEC104_MONTH_DEC)
933     {
934         // do in depth datetime analysis
935         if (sevenOctetBinaryTime->res3month.month == IEC104_MONTH_FEB)
936         {
937             // handle leap year first
938             if (isLeapYear(sevenOctetBinaryTime->res4year.year))
939             {
940                 if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_FEB_LEAPYEAR)
941                 {
942                     // "CP56Time2a Day of Month set outside of the allowable range for leap year
943                     DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
944                 }
945             }
946             else
947             {
948                 if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_FEB_NONLEAPYEAR)
949                 {
950                     // CP56Time2a Day of Month set outside of the allowable range for non-leap year
951                     DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
952                 }
953             }
954             // handle all months with 30 days
955         }
956         else if (sevenOctetBinaryTime->res3month.month == IEC104_MONTH_APR
957             or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_JUN
958             or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_SEP
959             or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_NOV)
960         {
961             if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_30)
962             {
963                 // CP56Time2a Day of Month set outside of the allowable range for 30-day months
964                 DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
965             }
966 
967         }// months with 31 days cannot be over as the type isn't large enough
968     }
969     else
970     {
971         // error indicating that parsing couldn't finish
972     }
973 }
974 
975 // Time2a Res3Month Structure
parseIec104Time2aRes3month(const Iec104Time2aRes3monthType * time2aRes3month)976 void parseIec104Time2aRes3month(const Iec104Time2aRes3monthType* time2aRes3month)
977 {
978     // ensure month is not over 12 (December)
979     if (time2aRes3month->month < IEC104_MONTH_JAN or time2aRes3month->month > IEC104_MONTH_DEC)
980     {
981         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MONTH);
982     }
983 
984     // provide an alert if the reserved field is used
985     if (time2aRes3month->res3)
986     {
987         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_MONTH);
988     }
989 }
990 
991 // Time2a Res4Year Structure
parseIec104Time2aRes4year(const Iec104Time2aRes4yearType * time2aRes4year)992 void parseIec104Time2aRes4year(const Iec104Time2aRes4yearType* time2aRes4year)
993 {
994     // ensure the year isn't before 1970 or after 2027
995     // the year field is treated as an offset from the year 1900
996     // so 1970 == 70 and 2027 == 127
997     // 2027 was chosen as an end date as the time2aRes4year->year field is only 7 bits
998     if ((int) time2aRes4year->year < IEC104_TIME2ARES4YEAR_1970)
999     {
1000         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_YEAR);
1001     }
1002 
1003     // provide an alert if the reserved field is used
1004     if (time2aRes4year->res4)
1005     {
1006         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_YEAR);
1007     }
1008 }
1009 
1010 // CP56Time2a Structure
parseIec104Cp56Time2a(const Iec104Cp56Time2aType * sevenOctetBinaryTime)1011 void parseIec104Cp56Time2a(const Iec104Cp56Time2aType* sevenOctetBinaryTime)
1012 {
1013     // Nothing worth alerting on directly here
1014 
1015     parseIec104Time2aMilliseconds(&sevenOctetBinaryTime->milliseconds);
1016     parseIec104Time2aIvresminute(&sevenOctetBinaryTime->ivresminute);
1017     parseIec104Time2aSures2hour(&sevenOctetBinaryTime->sures2hour);
1018     parseIec104Time2aDowday(sevenOctetBinaryTime); // need to pass the entire time struct for full error checking
1019     parseIec104Time2aRes3month(&sevenOctetBinaryTime->res3month);
1020     parseIec104Time2aRes4year(&sevenOctetBinaryTime->res4year);
1021 }
1022 
1023 // Cp24Time2a Structure
parseIec104Cp24Time2a(const Iec104Cp24Time2aType * threeOctetBinaryTime)1024 void parseIec104Cp24Time2a(const Iec104Cp24Time2aType* threeOctetBinaryTime)
1025 {
1026     // Nothing worth alerting on directly here
1027 
1028     parseIec104Time2aMilliseconds(&threeOctetBinaryTime->milliseconds);
1029     parseIec104Time2aIvresminute(&threeOctetBinaryTime->ivresminute);
1030 }
1031 
1032 // Cp16Time2a Structure
parseIec104Cp16Time2a(const Iec104Cp16Time2aType * cp16Time2a)1033 void parseIec104Cp16Time2a(const Iec104Cp16Time2aType* cp16Time2a)
1034 {
1035     // Nothing worth alerting on directly here
1036 
1037     parseIec104Time2aMilliseconds(&cp16Time2a->milliseconds);
1038 }
1039 
1040 // NOF: Name of File Structure
parseIec104Nof(const Iec104NofType * nof)1041 void parseIec104Nof(const Iec104NofType* nof)
1042 {
1043     // Nothing worth alerting on directly here
1044 
1045     if (!nof)
1046     {
1047         // error indicating that parsing couldn't finish
1048     }
1049 }
1050 
1051 // NOS: Name of Section Structure
parseIec104Nos(const Iec104NosType * nos)1052 void parseIec104Nos(const Iec104NosType* nos)
1053 {
1054     // Nothing worth alerting on directly here
1055 
1056     if (!nos)
1057     {
1058         // error indicating that parsing couldn't finish
1059     }
1060 }
1061 
1062 // LOF: Length of File or Section Structure
parseIec104Lof(const Iec104LofType * lof)1063 void parseIec104Lof(const Iec104LofType* lof)
1064 {
1065     // maybe a rule checking if length of file is greater than amount of data
1066     //  It appears that the length field here is an indicator for other messages actually containing the
1067     //  file data so detection may be better via plaintext rules with flowbits if desired
1068 
1069     if (!lof)
1070     {
1071         // error indicating that parsing couldn't finish
1072     }
1073 }
1074 
1075 // LOS: Length of Segment Structure
parseIec104Los(const Iec104LosType * los,uint16_t apduSize)1076 bool parseIec104Los(const Iec104LosType* los, uint16_t apduSize)
1077 {
1078     // flag to prevent debug parsing of the segments when an alert is thrown
1079     // doing this via a flag so that the debug messages for the LOS field still print
1080     bool losValid = true;
1081 
1082     // number of bytes counted in the length field before the LOS field
1083     uint16_t losPrecedingBytes = 0x11;
1084 
1085     // a segment length of zero is not expected
1086     if (los->lengthOfSegment == 0)
1087     {
1088         DetectionEngine::queue_event(GID_IEC104, IEC104_NULL_LOS_VALUE);
1089         losValid = false;
1090     }
1091     // since the los value indicates the number of octets in the segment and it is only used
1092     // in ASDU types that cannot have multiple number of items, the los value times 8 should
1093     // always equal the remaining number of bytes in the message
1094     // we can calculate this number by taking the apduSize (which has been checked for tampering
1095     // earlier) and subtracting the number of bytes preceding the los field (0x11)
1096     else if (los->lengthOfSegment != (apduSize - losPrecedingBytes))
1097     {
1098         DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_LOS_VALUE);
1099         losValid = false;
1100     }
1101 
1102     return losValid;
1103 }
1104 
1105 // CHS: Checksum Structure
parseIec104Chs(const Iec104ChsType * chs)1106 void parseIec104Chs(const Iec104ChsType* chs)
1107 {
1108     // Nothing worth alerting on directly here
1109 
1110     if (!chs)
1111     {
1112         // error indicating that parsing couldn't finish
1113     }
1114 }
1115 
1116 // SOF: Status of File Structure
parseIec104Sof(const Iec104SofType * sof)1117 void parseIec104Sof(const Iec104SofType* sof)
1118 {
1119     // provide an alert if the reserved field is used
1120     if (sof->sofStatus >= IEC104_SOF_STATUS_RES1)
1121     {
1122         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SOF);
1123     }
1124 }
1125 
1126 // QOS: Qualifier of Set Point Command Structure
parseIec104Qos(const Iec104QosType * qos)1127 void parseIec104Qos(const Iec104QosType* qos)
1128 {
1129     // provide an alert if the reserved field is used
1130     if (qos->ql >= IEC104_QOS_QL_RES1)
1131     {
1132         DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOS);
1133     }
1134 }
1135 
1136 // SCD: Status + Status Change Detection Structure
parseIec104Scd(const Iec104ScdType * scd)1137 void parseIec104Scd(const Iec104ScdType* scd)
1138 {
1139     // Nothing worth alerting on directly here
1140 
1141     if (!scd)
1142     {
1143         // error indicating that parsing couldn't finish
1144     }
1145 }
1146 
1147 // TSC: Test Sequence Counter
parseIec104Tsc(const Iec104TscType * tsc)1148 void parseIec104Tsc(const Iec104TscType* tsc)
1149 {
1150     // Nothing worth alerting on directly here
1151 
1152     if (!tsc)
1153     {
1154         // error indicating that parsing couldn't finish
1155     }
1156 }
1157 
1158 // Segment: Segment type
parseIec104Segment(const Iec104SegmentType * segment)1159 void parseIec104Segment(const Iec104SegmentType* segment)
1160 {
1161     // Nothing worth alerting on directly here
1162 
1163     if (!segment)
1164     {
1165         // error indicating that parsing couldn't finish
1166     }
1167 }
1168 
1169