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