Greenbone Vulnerability Management Libraries 22.29.3
cvss.c
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012-2023 Greenbone AG
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
72
73#include "cvss.h"
74
75#include <glib.h>
76#include <math.h>
77#include <strings.h>
78
79#undef G_LOG_DOMAIN
83#define G_LOG_DOMAIN "libgvm base"
84
85/* Static Headers. */
86
87static double
89
90static double
92
93/* CVSS v2. */
94
95// clang-format off
99#define AV_NETWORK 1.0
100#define AV_ADJACENT_NETWORK 0.646
101#define AV_LOCAL 0.395
102
106#define AC_LOW 0.71
107#define AC_MEDIUM 0.61
108#define AC_HIGH 0.35
109
113#define Au_MULTIPLE_INSTANCES 0.45
114#define Au_SINGLE_INSTANCE 0.56
115#define Au_NONE 0.704
116
120#define C_NONE 0.0
121#define C_PARTIAL 0.275
122#define C_COMPLETE 0.660
123
127#define I_NONE 0.0
128#define I_PARTIAL 0.275
129#define I_COMPLETE 0.660
130
134#define A_NONE 0.0
135#define A_PARTIAL 0.275
136#define A_COMPLETE 0.660
137// clang-format on
138
151
156{
157 const char *name;
158 double nvalue;
159};
160
173
174static const struct impact_item impact_map[][3] = {
175 [A] =
176 {
177 {"N", A_NONE},
178 {"P", A_PARTIAL},
179 {"C", A_COMPLETE},
180 },
181 [I] =
182 {
183 {"N", I_NONE},
184 {"P", I_PARTIAL},
185 {"C", I_COMPLETE},
186 },
187 [C] =
188 {
189 {"N", C_NONE},
190 {"P", C_PARTIAL},
191 {"C", C_COMPLETE},
192 },
193 [Au] =
194 {
195 {"N", Au_NONE},
197 {"S", Au_SINGLE_INSTANCE},
198 },
199 [AV] =
200 {
201 {"N", AV_NETWORK},
202 {"A", AV_ADJACENT_NETWORK},
203 {"L", AV_LOCAL},
204 },
205 [AC] =
206 {
207 {"L", AC_LOW},
208 {"M", AC_MEDIUM},
209 {"H", AC_HIGH},
210 },
211};
212
213// CVSS 4.0
214
259
263#define CVSS_METRICS_STR_BLANK "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
264
268#define CVSS_MACROVECTOR_BLANK "XXXXXX"
269
277typedef struct
278{
279 const char *metric_str;
281 const char *values;
283
295 // Base (11 metrics)
296 {"AV", CVSS4_AV, "NALP"},
297 {"AC", CVSS4_AC, "LH"},
298 {"AT", CVSS4_AT, "NP"},
299 {"PR", CVSS4_PR, "NLH"},
300 {"UI", CVSS4_UI, "NPA"},
301 {"VC", CVSS4_VC, "HLN"},
302 {"VI", CVSS4_VI, "HLN"},
303 {"VA", CVSS4_VA, "HLN"},
304 {"SC", CVSS4_SC, "HLN"},
305 {"SI", CVSS4_SI, "HLNS"},
306 {"SA", CVSS4_SA, "HLNS"},
307 // Threat (1 metric)
308 {"E", CVSS4_E, "XAPU"},
309 // Environmental (14 metrics)
310 {"CR", CVSS4_CR, "XHML"},
311 {"IR", CVSS4_IR, "XHML"},
312 {"AR", CVSS4_AR, "XHML"},
313 {"MAV", CVSS4_MAV, "XNALP"},
314 {"MAC", CVSS4_MAC, "XLH"},
315 {"MAT", CVSS4_MAT, "XNP"},
316 {"MPR", CVSS4_MPR, "XNLH"},
317 {"MUI", CVSS4_MUI, "XNPA"},
318 {"MVC", CVSS4_MVC, "XHLN"},
319 {"MVI", CVSS4_MVI, "XHLN"},
320 {"MVA", CVSS4_MVA, "XHLN"},
321 {"MSC", CVSS4_MSC, "XHLN"},
322 {"MSI", CVSS4_MSI, "XHLNS"},
323 {"MSA", CVSS4_MSA, "XHLNS"},
324 // Supplemental (6 metrics)
325 {"S", CVSS4_S, "XNP"},
326 {"AU", CVSS4_AU, "XNY"},
327 {"R", CVSS4_R, "XAUI"},
328 {"V", CVSS4_V, "XDC"},
329 {"RE", CVSS4_RE, "XLMH"},
330 {"U", CVSS4_U, "XCGAR"}, // Abbreviated to first letters
331 // Max number / array terminator
332 {NULL, CVSS4_METRICS_MAX, NULL}};
333
337typedef struct
338{
339 const char *vector;
340 double score;
342
351 {"000000", 10}, {"000001", 9.9}, {"000010", 9.8}, {"000011", 9.5},
352 {"000020", 9.5}, {"000021", 9.2}, {"000100", 10}, {"000101", 9.6},
353 {"000110", 9.3}, {"000111", 8.7}, {"000120", 9.1}, {"000121", 8.1},
354 {"000200", 9.3}, {"000201", 9}, {"000210", 8.9}, {"000211", 8},
355 {"000220", 8.1}, {"000221", 6.8}, {"001000", 9.8}, {"001001", 9.5},
356 {"001010", 9.5}, {"001011", 9.2}, {"001020", 9}, {"001021", 8.4},
357 {"001100", 9.3}, {"001101", 9.2}, {"001110", 8.9}, {"001111", 8.1},
358 {"001120", 8.1}, {"001121", 6.5}, {"001200", 8.8}, {"001201", 8},
359 {"001210", 7.8}, {"001211", 7}, {"001220", 6.9}, {"001221", 4.8},
360 {"002001", 9.2}, {"002011", 8.2}, {"002021", 7.2}, {"002101", 7.9},
361 {"002111", 6.9}, {"002121", 5}, {"002201", 6.9}, {"002211", 5.5},
362 {"002221", 2.7}, {"010000", 9.9}, {"010001", 9.7}, {"010010", 9.5},
363 {"010011", 9.2}, {"010020", 9.2}, {"010021", 8.5}, {"010100", 9.5},
364 {"010101", 9.1}, {"010110", 9}, {"010111", 8.3}, {"010120", 8.4},
365 {"010121", 7.1}, {"010200", 9.2}, {"010201", 8.1}, {"010210", 8.2},
366 {"010211", 7.1}, {"010220", 7.2}, {"010221", 5.3}, {"011000", 9.5},
367 {"011001", 9.3}, {"011010", 9.2}, {"011011", 8.5}, {"011020", 8.5},
368 {"011021", 7.3}, {"011100", 9.2}, {"011101", 8.2}, {"011110", 8},
369 {"011111", 7.2}, {"011120", 7}, {"011121", 5.9}, {"011200", 8.4},
370 {"011201", 7}, {"011210", 7.1}, {"011211", 5.2}, {"011220", 5},
371 {"011221", 3}, {"012001", 8.6}, {"012011", 7.5}, {"012021", 5.2},
372 {"012101", 7.1}, {"012111", 5.2}, {"012121", 2.9}, {"012201", 6.3},
373 {"012211", 2.9}, {"012221", 1.7}, {"100000", 9.8}, {"100001", 9.5},
374 {"100010", 9.4}, {"100011", 8.7}, {"100020", 9.1}, {"100021", 8.1},
375 {"100100", 9.4}, {"100101", 8.9}, {"100110", 8.6}, {"100111", 7.4},
376 {"100120", 7.7}, {"100121", 6.4}, {"100200", 8.7}, {"100201", 7.5},
377 {"100210", 7.4}, {"100211", 6.3}, {"100220", 6.3}, {"100221", 4.9},
378 {"101000", 9.4}, {"101001", 8.9}, {"101010", 8.8}, {"101011", 7.7},
379 {"101020", 7.6}, {"101021", 6.7}, {"101100", 8.6}, {"101101", 7.6},
380 {"101110", 7.4}, {"101111", 5.8}, {"101120", 5.9}, {"101121", 5},
381 {"101200", 7.2}, {"101201", 5.7}, {"101210", 5.7}, {"101211", 5.2},
382 {"101220", 5.2}, {"101221", 2.5}, {"102001", 8.3}, {"102011", 7},
383 {"102021", 5.4}, {"102101", 6.5}, {"102111", 5.8}, {"102121", 2.6},
384 {"102201", 5.3}, {"102211", 2.1}, {"102221", 1.3}, {"110000", 9.5},
385 {"110001", 9}, {"110010", 8.8}, {"110011", 7.6}, {"110020", 7.6},
386 {"110021", 7}, {"110100", 9}, {"110101", 7.7}, {"110110", 7.5},
387 {"110111", 6.2}, {"110120", 6.1}, {"110121", 5.3}, {"110200", 7.7},
388 {"110201", 6.6}, {"110210", 6.8}, {"110211", 5.9}, {"110220", 5.2},
389 {"110221", 3}, {"111000", 8.9}, {"111001", 7.8}, {"111010", 7.6},
390 {"111011", 6.7}, {"111020", 6.2}, {"111021", 5.8}, {"111100", 7.4},
391 {"111101", 5.9}, {"111110", 5.7}, {"111111", 5.7}, {"111120", 4.7},
392 {"111121", 2.3}, {"111200", 6.1}, {"111201", 5.2}, {"111210", 5.7},
393 {"111211", 2.9}, {"111220", 2.4}, {"111221", 1.6}, {"112001", 7.1},
394 {"112011", 5.9}, {"112021", 3}, {"112101", 5.8}, {"112111", 2.6},
395 {"112121", 1.5}, {"112201", 2.3}, {"112211", 1.3}, {"112221", 0.6},
396 {"200000", 9.3}, {"200001", 8.7}, {"200010", 8.6}, {"200011", 7.2},
397 {"200020", 7.5}, {"200021", 5.8}, {"200100", 8.6}, {"200101", 7.4},
398 {"200110", 7.4}, {"200111", 6.1}, {"200120", 5.6}, {"200121", 3.4},
399 {"200200", 7}, {"200201", 5.4}, {"200210", 5.2}, {"200211", 4},
400 {"200220", 4}, {"200221", 2.2}, {"201000", 8.5}, {"201001", 7.5},
401 {"201010", 7.4}, {"201011", 5.5}, {"201020", 6.2}, {"201021", 5.1},
402 {"201100", 7.2}, {"201101", 5.7}, {"201110", 5.5}, {"201111", 4.1},
403 {"201120", 4.6}, {"201121", 1.9}, {"201200", 5.3}, {"201201", 3.6},
404 {"201210", 3.4}, {"201211", 1.9}, {"201220", 1.9}, {"201221", 0.8},
405 {"202001", 6.4}, {"202011", 5.1}, {"202021", 2}, {"202101", 4.7},
406 {"202111", 2.1}, {"202121", 1.1}, {"202201", 2.4}, {"202211", 0.9},
407 {"202221", 0.4}, {"210000", 8.8}, {"210001", 7.5}, {"210010", 7.3},
408 {"210011", 5.3}, {"210020", 6}, {"210021", 5}, {"210100", 7.3},
409 {"210101", 5.5}, {"210110", 5.9}, {"210111", 4}, {"210120", 4.1},
410 {"210121", 2}, {"210200", 5.4}, {"210201", 4.3}, {"210210", 4.5},
411 {"210211", 2.2}, {"210220", 2}, {"210221", 1.1}, {"211000", 7.5},
412 {"211001", 5.5}, {"211010", 5.8}, {"211011", 4.5}, {"211020", 4},
413 {"211021", 2.1}, {"211100", 6.1}, {"211101", 5.1}, {"211110", 4.8},
414 {"211111", 1.8}, {"211120", 2}, {"211121", 0.9}, {"211200", 4.6},
415 {"211201", 1.8}, {"211210", 1.7}, {"211211", 0.7}, {"211220", 0.8},
416 {"211221", 0.2}, {"212001", 5.3}, {"212011", 2.4}, {"212021", 1.4},
417 {"212101", 2.4}, {"212111", 1.2}, {"212121", 0.5}, {"212201", 1},
418 {"212211", 0.3}, {"212221", 0.1}, {NULL, 0.0}};
419
427static GHashTable *cvss4_macrovector_table = NULL;
428
437static int
438toenum (const char *str, enum base_metrics *res)
439{
440 int rc = 0; /* let's be optimistic */
441
442 if (g_strcmp0 (str, "A") == 0)
443 *res = A;
444 else if (g_strcmp0 (str, "I") == 0)
445 *res = I;
446 else if (g_strcmp0 (str, "C") == 0)
447 *res = C;
448 else if (g_strcmp0 (str, "Au") == 0)
449 *res = Au;
450 else if (g_strcmp0 (str, "AU") == 0)
451 *res = Au;
452 else if (g_strcmp0 (str, "AV") == 0)
453 *res = AV;
454 else if (g_strcmp0 (str, "AC") == 0)
455 *res = AC;
456 else
457 rc = -1;
458
459 return rc;
460}
461
470static double
472{
473 return 10.41
474 * (1
475 - (1 - cvss->conf_impact) * (1 - cvss->integ_impact)
476 * (1 - cvss->avail_impact));
477}
478
487static double
493
503static inline int
504set_impact_from_str (const char *value, enum base_metrics metric,
505 struct cvss *cvss)
506{
507 int i;
508
509 for (i = 0; i < 3; i++)
510 {
511 const struct impact_item *impact;
512
513 impact = &impact_map[metric][i];
514
515 if (g_strcmp0 (impact->name, value) == 0)
516 {
517 switch (metric)
518 {
519 case A:
520 cvss->avail_impact = impact->nvalue;
521 break;
522
523 case I:
524 cvss->integ_impact = impact->nvalue;
525 break;
526
527 case C:
528 cvss->conf_impact = impact->nvalue;
529 break;
530
531 case Au:
532 cvss->authentication = impact->nvalue;
533 break;
534
535 case AV:
536 cvss->access_vector = impact->nvalue;
537 break;
538
539 case AC:
540 cvss->access_complexity = impact->nvalue;
541 break;
542
543 default:
544 return -1;
545 }
546 return 0;
547 }
548 }
549 return -1;
550}
551
560static double
562{
563 double impact = 1.176;
564 double impact_sub;
565 double exploitability_sub;
566
567 impact_sub = get_impact_subscore (cvss);
568 exploitability_sub = get_exploitability_subscore (cvss);
569
570 if (impact_sub < 0.1)
571 impact = 0.0;
572
573 return (((0.6 * impact_sub) + (0.4 * exploitability_sub) - 1.5) * impact)
574 + 0.0;
575}
576
584double
586{
587 struct cvss cvss;
588 char *token, *base_str, *base_metrics;
589
590 if (cvss_str == NULL)
591 return -1.0;
592
593 if (g_str_has_prefix (cvss_str, "CVSS:3.1/")
594 || g_str_has_prefix (cvss_str, "CVSS:3.0/"))
596 + strlen ("CVSS:3.X/"));
597 if (g_str_has_prefix (cvss_str, "CVSS:4.0/"))
598 return get_cvss_score_from_metrics_v4 (cvss_str + strlen ("CVSS:4.X/"));
599
600 memset (&cvss, 0x00, sizeof (struct cvss));
601
602 base_str = base_metrics = g_strdup_printf ("%s/", cvss_str);
603
604 while ((token = strchr (base_metrics, '/')) != NULL)
605 {
606 char *token2 = strtok (base_metrics, ":");
607 char *metric_name = token2;
608 char *metric_value;
609 enum base_metrics mval;
610 int rc;
611
612 *token++ = '\0';
613
614 if (metric_name == NULL)
615 goto ret_err;
616
617 metric_value = strtok (NULL, ":");
618
619 if (metric_value == NULL)
620 goto ret_err;
621
622 rc = toenum (metric_name, &mval);
623 if (rc)
624 goto ret_err;
625
626 if (set_impact_from_str (metric_value, mval, &cvss))
627 goto ret_err;
628
629 base_metrics = token;
630 }
631
632 g_free (base_str);
633 return __get_cvss_score (&cvss);
634
635ret_err:
636 g_free (base_str);
637 return (double) -1;
638}
639
640/* CVSS v3. */
641
649static double
650roundup (double cvss)
651{
652 int trim;
653
654 /* "Roundup returns the smallest number, specified to 1 decimal place,
655 * that is equal to or higher than its input. For example, Roundup (4.02)
656 * returns 4.1; and Roundup (4.00) returns 4.0." */
657
658 /* 3.020000000 => 3.1 */
659 /* 3.000000001 => 3.0 */
660 /* 5.299996 => 5.3 */
661 /* 5.500320 => 5.6 */
662
663 trim = round (cvss * 100000);
664 if ((trim % 10000) == 0)
665 return ((double) trim) / 100000;
666 return (floor (trim / 10000) + 1) / 10.0;
667}
668
676static double
677v3_impact (const char *value)
678{
679 if (strcasecmp (value, "N") == 0)
680 return 0.0;
681 if (strcasecmp (value, "L") == 0)
682 return 0.22;
683 if (strcasecmp (value, "H") == 0)
684 return 0.56;
685 return -1.0;
686}
687
695static double
697{
698 gchar **split, **point;
699 int scope_changed;
700 double impact_conf, impact_integ, impact_avail;
701 double vector, complexity, privilege, user;
702 double isc_base, impact, exploitability, base;
703
704 /* https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
705 * https://www.first.org/cvss/v3.1/specification-document
706 * https://www.first.org/cvss/v3.0/specification-document */
707
708 scope_changed = -1;
709 impact_conf = -1.0;
710 impact_integ = -1.0;
711 impact_avail = -1.0;
712 vector = -1.0;
713 complexity = -1.0;
714 privilege = -1.0;
715 user = -1.0;
716
717 /* AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N */
718
719 split = g_strsplit (cvss_str, "/", 0);
720 point = split;
721 while (*point)
722 {
723 /* Scope. */
724 if (strncasecmp ("S:", *point, 2) == 0)
725 {
726 if (strcasecmp (*point + 2, "U") == 0)
727 scope_changed = 0;
728 else if (strcasecmp (*point + 2, "C") == 0)
729 scope_changed = 1;
730 }
731
732 /* Confidentiality. */
733 if (strncasecmp ("C:", *point, 2) == 0)
734 impact_conf = v3_impact (*point + 2);
735
736 /* Integrity. */
737 if (strncasecmp ("I:", *point, 2) == 0)
738 impact_integ = v3_impact (*point + 2);
739
740 /* Availability. */
741 if (strncasecmp ("A:", *point, 2) == 0)
742 impact_avail = v3_impact (*point + 2);
743
744 /* Attack Vector. */
745 if (strncasecmp ("AV:", *point, 3) == 0)
746 {
747 if (strcasecmp (*point + 3, "N") == 0)
748 vector = 0.85;
749 else if (strcasecmp (*point + 3, "A") == 0)
750 vector = 0.62;
751 else if (strcasecmp (*point + 3, "L") == 0)
752 vector = 0.55;
753 else if (strcasecmp (*point + 3, "P") == 0)
754 vector = 0.2;
755 }
756
757 /* Attack Complexity. */
758 if (strncasecmp ("AC:", *point, 3) == 0)
759 {
760 if (strcasecmp (*point + 3, "L") == 0)
761 complexity = 0.77;
762 else if (strcasecmp (*point + 3, "H") == 0)
763 complexity = 0.44;
764 }
765
766 /* Privileges Required. */
767 if (strncasecmp ("PR:", *point, 3) == 0)
768 {
769 if (strcasecmp (*point + 3, "N") == 0)
770 privilege = 0.85;
771 else if (strcasecmp (*point + 3, "L") == 0)
772 privilege = 0.62;
773 else if (strcasecmp (*point + 3, "H") == 0)
774 privilege = 0.27;
775 else
776 privilege = -1.0;
777 }
778
779 /* User Interaction. */
780 if (strncasecmp ("UI:", *point, 3) == 0)
781 {
782 if (strcasecmp (*point + 3, "N") == 0)
783 user = 0.85;
784 else if (strcasecmp (*point + 3, "R") == 0)
785 user = 0.62;
786 }
787
788 point++;
789 }
790
791 g_strfreev (split);
792
793 /* All of the base metrics are required. */
794
795 if (scope_changed == -1 || impact_conf == -1.0 || impact_integ == -1.0
796 || impact_avail == -1.0 || vector == -1.0 || complexity == -1.0
797 || privilege == -1.0 || user == -1.0)
798 return -1.0;
799
800 /* Privileges Required has a special case for S:C. */
801
802 if (scope_changed && privilege == 0.62)
803 privilege = 0.68;
804 else if (scope_changed && privilege == 0.27)
805 privilege = 0.5;
806
807 /* Impact. */
808
809 isc_base = 1 - ((1 - impact_conf) * (1 - impact_integ) * (1 - impact_avail));
810
811 if (scope_changed)
812 impact = 7.52 * (isc_base - 0.029) - 3.25 * pow ((isc_base - 0.02), 15);
813 else
814 impact = 6.42 * isc_base;
815
816 if (impact <= 0)
817 return 0.0;
818
819 /* Exploitability. */
820
821 exploitability = 8.22 * vector * complexity * privilege * user;
822
823 /* Final. */
824
825 if (scope_changed)
826 base = 1.08 * (impact + exploitability);
827 else
828 base = impact + exploitability;
829
830 if (base > 10.0)
831 return 10.0;
832
833 return roundup (base);
834}
835
839static void
841{
843 return;
844
845 int index = 0;
846 cvss4_macrovector_table = g_hash_table_new (g_str_hash, g_str_equal);
847 while (cvss4_macrovector_mappings[index].vector != NULL)
848 {
849 g_hash_table_insert (cvss4_macrovector_table,
850 (gpointer) cvss4_macrovector_mappings[index].vector,
851 (gpointer) &cvss4_macrovector_mappings[index].score);
852
853 index++;
854 }
855}
856
864static inline double
865cvss4_macrovector_score (const char *vector)
866{
867 double *score_ptr;
868
870 score_ptr = g_hash_table_lookup (cvss4_macrovector_table, vector);
871 if (score_ptr)
872 return *score_ptr;
873 return -1.0;
874}
875
887static char
888cvss4_m (const char *simplified_vec, cvss4_metric_t metric)
889{
890 char selected = simplified_vec[metric];
891
892 // If E=X it will default to the worst case i.e. E=A
893 if (metric == CVSS4_E && selected == 'X')
894 return 'A';
895
896 // If CR=X, IR=X or AR=X they will default to the worst case
897 // i.e. CR=H, IR=H and AR=H
898 if ((metric == CVSS4_CR || metric == CVSS4_IR || metric == CVSS4_AR)
899 && selected == 'X')
900 return 'H';
901
902 // All other environmental metrics just overwrite base score values,
903 // so if they’re not defined just use the base score value.
904 if (metric >= CVSS4_AV && metric <= CVSS4_SA)
905 {
906 char modified_selected = simplified_vec[metric - CVSS4_AV + CVSS4_MAV];
907 if (modified_selected != 'X')
908 return modified_selected;
909 }
910
911 return selected;
912}
913
928static gchar *
929simplify_cvss4_vector (const char *cvss_str)
930{
931 gchar **split_cvss_str, **split_cvss_point;
932 gboolean valid = TRUE;
933 gchar *vec = NULL;
934 cvss4_metric_t metric;
935
936 if (cvss_str == NULL || strcmp (cvss_str, "") == 0)
937 return NULL;
938
939 vec = g_strdup (CVSS_METRICS_STR_BLANK);
940
941 split_cvss_str = g_strsplit (cvss_str, "/", -1);
942 split_cvss_point = split_cvss_str;
943 while (valid && *split_cvss_point)
944 {
945 if (strcmp (*split_cvss_point, "") == 0)
946 {
947 split_cvss_point++;
948 continue;
949 }
950
951 gchar **split_component = g_strsplit (*split_cvss_point, ":", 2);
952 const gchar *metric_str = split_component[0], *value = split_component[1];
953
954 valid = FALSE;
955
956 if (value == NULL)
957 {
958 g_debug ("%s: value for metric %s missing", __func__, metric_str);
959 g_strfreev (split_component);
960 break;
961 }
962 else if (strcasecmp (metric_str, "U") == 0)
963 {
964 // Special case for the Provider Urgency metric
965 if (strcasecmp (value, "Red") && strcasecmp (value, "Amber")
966 && strcasecmp (value, "Green") && strcasecmp (value, "Clear")
967 && strcasecmp (value, "X"))
968 {
969 g_debug ("%s: value for metric %s must be one of"
970 " 'Red', 'Amber', 'Green', 'Clear', 'X'",
971 __func__, metric_str);
972 g_strfreev (split_component);
973 break;
974 }
975 else
976 valid = TRUE;
977 }
978 else if (strlen (value) != 1)
979 {
980 g_debug ("%s: value for metric %s must be 1 character", __func__,
981 metric_str);
982 g_strfreev (split_component);
983 break;
984 }
985
986 cvss4_metric_def_t *metric_def = &cvss4_metric_defs[0];
987 while (metric_def->metric_str)
988 {
989 if (strcasecmp (metric_str, metric_def->metric_str) == 0)
990 {
991 char value_char = g_ascii_toupper (value[0]);
992
993 // Reject duplicate metrics
994 if (vec[metric_def->metric] != 'X')
995 {
996 g_debug ("%s: duplicate metric %s", __func__, metric_str);
997 break;
998 }
999
1000 // Set the metric in the simplified vector
1001 if (strchr (metric_def->values, value_char))
1002 {
1003 valid = TRUE;
1004 vec[metric_def->metric] = value_char;
1005 }
1006 else
1007 {
1008 g_debug ("%s: invalid metric: %s:%c", __func__, metric_str,
1009 value_char);
1010 }
1011 break;
1012 }
1013 metric_def++;
1014 }
1015
1016 split_cvss_point++;
1017 g_strfreev (split_component);
1018 }
1019 g_strfreev (split_cvss_str);
1020
1021 for (metric = CVSS4_AV; valid && metric <= CVSS4_SA; metric++)
1022 {
1023 if (vec[metric] == 'X')
1024 {
1025 g_debug ("%s: mandatory metric %s is undefined", __func__,
1026 cvss4_metric_defs[metric].metric_str);
1027 valid = FALSE;
1028 }
1029 }
1030
1031 if (!valid)
1032 {
1033 g_debug ("%s: vector %s is invalid", __func__, cvss_str);
1034 g_free (vec);
1035 return NULL;
1036 }
1037
1038 return vec;
1039}
1040
1048static gchar *
1049cvss4_vector_expand (const char *vec)
1050{
1051 cvss4_metric_t metric;
1052 GString *str = g_string_new ("CVSS:4.0");
1053 for (metric = 0; metric < CVSS4_METRICS_MAX; metric++)
1054 {
1055 const char *expanded_value;
1056 if (vec[metric] == 'X')
1057 continue;
1059 if (metric == CVSS4_U)
1060 {
1061 switch (vec[metric])
1062 {
1063 case 'R':
1064 expanded_value = "Red";
1065 break;
1066 case 'A':
1067 expanded_value = "Amber";
1068 break;
1069 case 'G':
1070 expanded_value = "Green";
1071 break;
1072 case 'C':
1073 expanded_value = "Clear";
1074 break;
1075 default:
1076 expanded_value = NULL;
1077 }
1078 }
1079 else
1080 expanded_value = NULL;
1081
1082 if (expanded_value)
1083 g_string_append_printf (str, "/%s:%s", def.metric_str, expanded_value);
1084 else
1085 g_string_append_printf (str, "/%s:%c", def.metric_str, vec[metric]);
1086 }
1087 return g_string_free (str, FALSE);
1088}
1089
1097static inline gchar *
1098cvss4_macrovector (const char *vec)
1099{
1100 gchar *macrovector;
1101 if (vec == NULL)
1102 return NULL;
1103
1104 macrovector = g_strdup (CVSS_MACROVECTOR_BLANK);
1105
1106 // EQ1: 0-AV:N and PR:N and UI:N
1107 // 1-(AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P
1108 // 2-AV:P or not(AV:N or PR:N or UI:N)
1109 char av = cvss4_m (vec, CVSS4_AV);
1110 char pr = cvss4_m (vec, CVSS4_PR);
1111 char ui = cvss4_m (vec, CVSS4_UI);
1112
1113 if (av == 'N' && pr == 'N' && ui == 'N')
1114 macrovector[0] = '0';
1115 else if ((av == 'N' || pr == 'N' || ui == 'N') && !(av == 'P'))
1116 macrovector[0] = '1';
1117 else
1118 macrovector[0] = '2';
1119
1120 // EQ2: 0-(AC:L and AT:N)
1121 // 1-(not(AC:L and AT:N))
1122 char ac = cvss4_m (vec, CVSS4_AC);
1123 char at = cvss4_m (vec, CVSS4_AT);
1124
1125 if (ac == 'L' && at == 'N')
1126 macrovector[1] = '0';
1127 else
1128 macrovector[1] = '1';
1129
1130 // EQ3: 0-(VC:H and VI:H)
1131 // 1-(not(VC:H and VI:H) and (VC:H or VI:H or VA:H))
1132 // 2-not (VC:H or VI:H or VA:H)
1133 char vc = cvss4_m (vec, CVSS4_VC);
1134 char vi = cvss4_m (vec, CVSS4_VI);
1135 char va = cvss4_m (vec, CVSS4_VA);
1136
1137 if (vc == 'H' && vi == 'H')
1138 macrovector[2] = '0';
1139 else if (vc == 'H' || vi == 'H' || va == 'H')
1140 macrovector[2] = '1';
1141 else
1142 macrovector[2] = '2';
1143
1144 // EQ4: 0-(MSI:S or MSA:S)
1145 // 1-not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H)
1146 // 2-not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H)
1147 //
1148 // "Effective" SI and SA are the same as MSI and MSA for the purposes of
1149 // checking for the "Safety" value.
1150 char sc = cvss4_m (vec, CVSS4_SC);
1151 char si = cvss4_m (vec, CVSS4_SI);
1152 char sa = cvss4_m (vec, CVSS4_SA);
1153 if (si == 'S' || sa == 'S')
1154 macrovector[3] = '0';
1155 else if (sc == 'H' || si == 'H' || sa == 'H')
1156 macrovector[3] = '1';
1157 else
1158 macrovector[3] = '2';
1159
1160 // EQ5: 0-E:A
1161 // 1-E:P
1162 // 2-E:U
1163 char e = cvss4_m (vec, CVSS4_E);
1164 if (e == 'A')
1165 macrovector[4] = '0';
1166 else if (e == 'P')
1167 macrovector[4] = '1';
1168 else
1169 macrovector[4] = '2';
1170
1171 // EQ6: 0-(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)
1172 // 1-not[(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)]
1173 char cr = cvss4_m (vec, CVSS4_CR);
1174 char ir = cvss4_m (vec, CVSS4_IR);
1175 char ar = cvss4_m (vec, CVSS4_AR);
1176 if ((cr == 'H' && vc == 'H') || (ir == 'H' && vi == 'H')
1177 || (ar == 'H' && va == 'H'))
1178 macrovector[5] = '0';
1179 else
1180 macrovector[5] = '1';
1181
1182 return macrovector;
1183}
1184
1195static void
1196cvss4_maximal_scoring_differences (const char *macrovector,
1197 double *available_distance_eq1,
1198 double *available_distance_eq2,
1199 double *available_distance_eq3eq6,
1200 double *available_distance_eq4,
1201 double *available_distance_eq5)
1202{
1203 double value = cvss4_macrovector_score (macrovector);
1204 double score_eq1_next_lower_macro, score_eq2_next_lower_macro;
1205 double score_eq3eq6_next_lower_macro;
1206 double score_eq4_next_lower_macro, score_eq5_next_lower_macro;
1207
1208 // Next lower macrovector for EQ1 only exists if EQ1 is 0 or 1.
1209 if (macrovector[0] <= '1')
1210 {
1211 gchar *eq1_next_lower_macro = g_strdup (macrovector);
1212 eq1_next_lower_macro[0]++;
1213 score_eq1_next_lower_macro =
1214 cvss4_macrovector_score (eq1_next_lower_macro);
1215 g_free (eq1_next_lower_macro);
1216 }
1217 else
1218 score_eq1_next_lower_macro = -1.0;
1219
1220 // Next lower macrovector for EQ2 only exists if EQ2 is 0.
1221 if (macrovector[1] == '0')
1222 {
1223 gchar *eq2_next_lower_macro = g_strdup (macrovector);
1224 eq2_next_lower_macro[1]++;
1225 score_eq2_next_lower_macro =
1226 cvss4_macrovector_score (eq2_next_lower_macro);
1227 g_free (eq2_next_lower_macro);
1228 }
1229 else
1230 score_eq2_next_lower_macro = -1.0;
1231
1232 // Next lower macrovector for EQ3.
1233 if ((macrovector[2] == '0' || macrovector[2] == '1') && macrovector[5] == '1')
1234 {
1235 gchar *eq3eq6_next_lower_macro = g_strdup (macrovector);
1236 eq3eq6_next_lower_macro[2]++;
1237 score_eq3eq6_next_lower_macro =
1238 cvss4_macrovector_score (eq3eq6_next_lower_macro);
1239 g_free (eq3eq6_next_lower_macro);
1240 }
1241 else if (macrovector[2] == '1' && macrovector[5] == '0')
1242 {
1243 gchar *eq3eq6_next_lower_macro = g_strdup (macrovector);
1244 eq3eq6_next_lower_macro[5]++;
1245 score_eq3eq6_next_lower_macro =
1246 cvss4_macrovector_score (eq3eq6_next_lower_macro);
1247 g_free (eq3eq6_next_lower_macro);
1248 }
1249 else if (macrovector[2] == '0' && macrovector[5] == '0')
1250 {
1251 gchar *eq3eq6_next_lower_macro_left = g_strdup (macrovector);
1252 eq3eq6_next_lower_macro_left[5]++;
1253 gchar *eq3eq6_next_lower_macro_right = g_strdup (macrovector);
1254 eq3eq6_next_lower_macro_right[2]++;
1255 double score_eq3eq6_next_lower_macro_left =
1256 cvss4_macrovector_score (eq3eq6_next_lower_macro_left);
1257 double score_eq3eq6_next_lower_macro_right =
1258 cvss4_macrovector_score (eq3eq6_next_lower_macro_right);
1259
1260 if (score_eq3eq6_next_lower_macro_left
1261 > score_eq3eq6_next_lower_macro_right)
1262 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_left;
1263 else
1264 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_right;
1265
1266 g_free (eq3eq6_next_lower_macro_left);
1267 g_free (eq3eq6_next_lower_macro_right);
1268 }
1269 else
1270 score_eq3eq6_next_lower_macro = -1.0;
1271
1272 // Next lower macrovector for EQ4 only exists if EQ4 is 0 or 1.
1273 if (macrovector[3] <= '1')
1274 {
1275 gchar *eq4_next_lower_macro = g_strdup (macrovector);
1276 eq4_next_lower_macro[3]++;
1277 score_eq4_next_lower_macro =
1278 cvss4_macrovector_score (eq4_next_lower_macro);
1279 g_free (eq4_next_lower_macro);
1280 }
1281 else
1282 score_eq4_next_lower_macro = -1.0;
1283
1284 // Next lower macrovector for EQ5 only exists if EQ5 is 0 or 1.
1285 if (macrovector[4] <= '1')
1286 {
1287 gchar *eq5_next_lower_macro = g_strdup (macrovector);
1288 eq5_next_lower_macro[4]++;
1289 score_eq5_next_lower_macro =
1290 cvss4_macrovector_score (eq5_next_lower_macro);
1291 g_free (eq5_next_lower_macro);
1292 }
1293 else
1294 score_eq5_next_lower_macro = -1.0;
1295
1296 if (value != -1.0)
1297 {
1298 *available_distance_eq1 = score_eq1_next_lower_macro != -1.0
1299 ? value - score_eq1_next_lower_macro
1300 : -1.0;
1301 *available_distance_eq2 = score_eq2_next_lower_macro != -1.0
1302 ? value - score_eq2_next_lower_macro
1303 : -1.0;
1304 *available_distance_eq3eq6 = score_eq3eq6_next_lower_macro != -1.0
1305 ? value - score_eq3eq6_next_lower_macro
1306 : -1.0;
1307 *available_distance_eq4 = score_eq4_next_lower_macro != -1.0
1308 ? value - score_eq4_next_lower_macro
1309 : -1.0;
1310 *available_distance_eq5 = score_eq5_next_lower_macro != -1.0
1311 ? value - score_eq5_next_lower_macro
1312 : -1.0;
1313 }
1314 else
1315 {
1316 *available_distance_eq1 = -1.0;
1317 *available_distance_eq2 = -1.0;
1318 *available_distance_eq3eq6 = -1.0;
1319 *available_distance_eq4 = -1.0;
1320 *available_distance_eq5 = -1.0;
1321 }
1322}
1323
1331static gchar **
1332cvss4_max_vectors (const char *macrovector)
1333{
1334 const char **eq1_maxes, **eq2_maxes, **eq3eq6_maxes;
1335 const char *eq4_max, *eq5_max;
1336 gchar **ret;
1337
1338 // EQ1
1339 static const char *eq1_maxes_0[] = {"AV:N/PR:N/UI:N/", NULL};
1340 static const char *eq1_maxes_1[] = {"AV:A/PR:N/UI:N/", "AV:N/PR:L/UI:N/",
1341 "AV:N/PR:N/UI:P/", NULL};
1342 static const char *eq1_maxes_2[] = {"AV:P/PR:N/UI:N/", "AV:A/PR:L/UI:P/",
1343 NULL};
1344 if (macrovector[0] == '0')
1345 eq1_maxes = eq1_maxes_0;
1346 else if (macrovector[0] == '1')
1347 eq1_maxes = eq1_maxes_1;
1348 else
1349 eq1_maxes = eq1_maxes_2;
1350
1351 // EQ2
1352 static const char *eq2_maxes_0[] = {"AC:L/AT:N/", NULL};
1353 static const char *eq2_maxes_1[] = {"AC:H/AT:N/", "AC:L/AT:P/", NULL};
1354 if (macrovector[1] == '0')
1355 eq2_maxes = eq2_maxes_0;
1356 else
1357 eq2_maxes = eq2_maxes_1;
1358
1359 // EQ3+EQ6
1360 static const char *eq3eq6_maxes_00[] = {"VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/",
1361 NULL};
1362 static const char *eq3eq6_maxes_01[] = {
1363 "VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/", "VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/", NULL};
1364 static const char *eq3eq6_maxes_10[] = {
1365 "VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/", "VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/", NULL};
1366 static const char *eq3eq6_maxes_11[] = {
1367 "VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/", "VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/",
1368 "VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/", "VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/",
1369 "VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/", NULL};
1370 static const char *eq3eq6_maxes_21[] = {"VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/",
1371 NULL};
1372 if ((macrovector[2] == '0'))
1373 {
1374 if (macrovector[5] == '0')
1375 eq3eq6_maxes = eq3eq6_maxes_00;
1376 else
1377 eq3eq6_maxes = eq3eq6_maxes_01;
1378 }
1379 else if ((macrovector[2] == '1'))
1380 {
1381 if (macrovector[5] == '0')
1382 eq3eq6_maxes = eq3eq6_maxes_10;
1383 else
1384 eq3eq6_maxes = eq3eq6_maxes_11;
1385 }
1386 else
1387 eq3eq6_maxes = eq3eq6_maxes_21;
1388
1389 // EQ4
1390 if (macrovector[3] == '0')
1391 eq4_max = "SC:H/SI:S/SA:S/";
1392 else if (macrovector[3] == '1')
1393 eq4_max = "SC:H/SI:H/SA:H/";
1394 else
1395 eq4_max = "SC:L/SI:L/SA:L/";
1396
1397 // EQ5
1398 if (macrovector[4] == '0')
1399 eq5_max = "E:A/";
1400 else if (macrovector[4] == '1')
1401 eq5_max = "E:P/";
1402 else
1403 eq5_max = "E:U/";
1404
1405 GPtrArray *max_vectors = g_ptr_array_new ();
1406 const char **eq1_max, **eq2_max, **eq3eq6_max;
1407 for (eq1_max = eq1_maxes; *eq1_max != NULL; eq1_max++)
1408 {
1409 for (eq2_max = eq2_maxes; *eq2_max != NULL; eq2_max++)
1410 {
1411 for (eq3eq6_max = eq3eq6_maxes; *eq3eq6_max != NULL; eq3eq6_max++)
1412 {
1413 gchar *full_vector =
1414 g_strdup_printf ("%s%s%s%s%s", *eq1_max, *eq2_max, *eq3eq6_max,
1415 eq4_max, eq5_max);
1416 gchar *vector = simplify_cvss4_vector (full_vector);
1417 if (vector == NULL)
1418 g_warning ("%s: generated vector %s is invalid", __func__,
1419 full_vector);
1420 else
1421 g_ptr_array_add (max_vectors, vector);
1422 g_free (full_vector);
1423 }
1424 }
1425 }
1426
1427 g_ptr_array_add (max_vectors, NULL);
1428
1429 ret = (gchar **) max_vectors->pdata;
1430 g_ptr_array_free (max_vectors, FALSE);
1431 return ret;
1432}
1433
1442static double
1444{
1445 switch (metric)
1446 {
1447 case CVSS4_AV:
1448 switch (value)
1449 {
1450 case 'N':
1451 return 0.0;
1452 case 'A':
1453 return 0.1;
1454 case 'L':
1455 return 0.2;
1456 case 'P':
1457 return 0.3;
1458 default:
1459 return -99.0;
1460 }
1461 break;
1462 case CVSS4_PR:
1463 switch (value)
1464 {
1465 case 'N':
1466 return 0.0;
1467 case 'L':
1468 return 0.1;
1469 case 'H':
1470 return 0.2;
1471 default:
1472 return -99.0;
1473 }
1474 break;
1475 case CVSS4_UI:
1476 switch (value)
1477 {
1478 case 'N':
1479 return 0.0;
1480 case 'P':
1481 return 0.1;
1482 case 'A':
1483 return 0.2;
1484 default:
1485 return -99.0;
1486 }
1487 break;
1488 case CVSS4_AC:
1489 switch (value)
1490 {
1491 case 'L':
1492 return 0.0;
1493 case 'H':
1494 return 0.1;
1495 default:
1496 return -99.0;
1497 }
1498 break;
1499 case CVSS4_AT:
1500 switch (value)
1501 {
1502 case 'N':
1503 return 0.0;
1504 case 'P':
1505 return 0.1;
1506 default:
1507 return -99.0;
1508 }
1509 break;
1510 case CVSS4_VC:
1511 case CVSS4_VI:
1512 case CVSS4_VA:
1513 switch (value)
1514 {
1515 case 'H':
1516 return 0.0;
1517 case 'L':
1518 return 0.1;
1519 case 'N':
1520 return 0.2;
1521 default:
1522 return -99.0;
1523 }
1524 break;
1525 case CVSS4_SC:
1526 case CVSS4_SI:
1527 case CVSS4_SA:
1528 switch (value)
1529 {
1530 case 'S':
1531 return 0.0;
1532 case 'H':
1533 return 0.1;
1534 case 'L':
1535 return 0.2;
1536 case 'N':
1537 return 0.3;
1538 default:
1539 return -99.0;
1540 }
1541 break;
1542 case CVSS4_CR:
1543 case CVSS4_IR:
1544 case CVSS4_AR:
1545 switch (value)
1546 {
1547 case 'H':
1548 return 0.0;
1549 case 'M':
1550 return 0.1;
1551 case 'L':
1552 return 0.2;
1553 default:
1554 return -99.0;
1555 }
1556 break;
1557
1558 // The Exploit Maturity metric is included in the reference implementation
1559 // but never used
1560 /*
1561 case CVSS4_E:
1562 switch (value)
1563 {
1564 case 'A': return 0.0;
1565 case 'P': return 0.1;
1566 case 'U': return 0.2;
1567 }
1568 break;
1569 */
1570 default:
1571 return -99.0;
1572 }
1573}
1574
1584static inline double
1586 const char *max_vec)
1587{
1588 return cvss4_metric_level (metric, cvss4_m (vec, metric))
1589 - cvss4_metric_level (metric, max_vec[metric]);
1590}
1591
1603static void
1604cvss4_current_severity_distances (const char *vec, const char *macrovector,
1605 double *current_severity_distance_eq1,
1606 double *current_severity_distance_eq2,
1607 double *current_severity_distance_eq3eq6,
1608 double *current_severity_distance_eq4,
1609 double *current_severity_distance_eq5)
1610{
1611 double severity_distance_AV, severity_distance_PR, severity_distance_UI;
1612 double severity_distance_AC, severity_distance_AT;
1613 double severity_distance_VC, severity_distance_VI, severity_distance_VA;
1614 double severity_distance_SC, severity_distance_SI, severity_distance_SA;
1615 double severity_distance_CR, severity_distance_IR, severity_distance_AR;
1616
1617 severity_distance_AV = severity_distance_PR = severity_distance_UI = -99.0;
1618 severity_distance_AC = severity_distance_AT = -99.0;
1619 severity_distance_VC = severity_distance_VI = severity_distance_VA = -99.0;
1620 severity_distance_SC = severity_distance_SI = severity_distance_SA = -99.0;
1621 severity_distance_CR = severity_distance_IR = severity_distance_AR = -99.0;
1622
1623 char **max_vectors, **max_vec;
1624 max_vectors = cvss4_max_vectors (macrovector);
1625 for (max_vec = max_vectors; *max_vec != NULL; max_vec++)
1626 {
1627 severity_distance_AV = cvss4_severity_distance (CVSS4_AV, vec, *max_vec);
1628 severity_distance_PR = cvss4_severity_distance (CVSS4_PR, vec, *max_vec);
1629 severity_distance_UI = cvss4_severity_distance (CVSS4_UI, vec, *max_vec);
1630
1631 severity_distance_AC = cvss4_severity_distance (CVSS4_AC, vec, *max_vec);
1632 severity_distance_AT = cvss4_severity_distance (CVSS4_AT, vec, *max_vec);
1633
1634 severity_distance_VC = cvss4_severity_distance (CVSS4_VC, vec, *max_vec);
1635 severity_distance_VI = cvss4_severity_distance (CVSS4_VI, vec, *max_vec);
1636 severity_distance_VA = cvss4_severity_distance (CVSS4_VA, vec, *max_vec);
1637
1638 severity_distance_SC = cvss4_severity_distance (CVSS4_SC, vec, *max_vec);
1639 severity_distance_SI = cvss4_severity_distance (CVSS4_SI, vec, *max_vec);
1640 severity_distance_SA = cvss4_severity_distance (CVSS4_SA, vec, *max_vec);
1641
1642 severity_distance_CR = cvss4_severity_distance (CVSS4_CR, vec, *max_vec);
1643 severity_distance_IR = cvss4_severity_distance (CVSS4_IR, vec, *max_vec);
1644 severity_distance_AR = cvss4_severity_distance (CVSS4_AR, vec, *max_vec);
1645
1646 if (severity_distance_AV < 0.0 || severity_distance_PR < 0.0
1647 || severity_distance_UI < 0.0 || severity_distance_AC < 0.0
1648 || severity_distance_AT < 0.0 || severity_distance_VC < 0.0
1649 || severity_distance_VI < 0.0 || severity_distance_VA < 0.0
1650 || severity_distance_SC < 0.0 || severity_distance_SI < 0.0
1651 || severity_distance_SA < 0.0 || severity_distance_CR < 0.0
1652 || severity_distance_IR < 0.0 || severity_distance_AR < 0.0)
1653 continue;
1654
1655 g_debug ("%s AV:%0.1f PR:%0.1f UI:%0.1f |"
1656 " AC:%0.1f AT:%0.1f |"
1657 " VC:%0.1f VI:%0.1f VA:%0.1f |"
1658 " SC:%0.1f SI:%0.1f SA:%0.1f |"
1659 " CR:%0.1f IR:%0.1f AR:%0.1f",
1660 __func__, severity_distance_AV, severity_distance_PR,
1661 severity_distance_UI, severity_distance_AC, severity_distance_AT,
1662 severity_distance_VC, severity_distance_VI, severity_distance_VA,
1663 severity_distance_SC, severity_distance_SI, severity_distance_SA,
1664 severity_distance_CR, severity_distance_IR,
1665 severity_distance_AR);
1666 break;
1667 }
1668
1669 gchar *max_vec_expanded = cvss4_vector_expand (*max_vec);
1670 g_debug ("%s: max_vec: %s", __func__, max_vec_expanded);
1671 g_free (max_vec_expanded);
1672 g_strfreev (max_vectors);
1673
1674 *current_severity_distance_eq1 =
1675 severity_distance_AV + severity_distance_PR + severity_distance_UI;
1676 *current_severity_distance_eq2 = severity_distance_AC + severity_distance_AT;
1677 *current_severity_distance_eq3eq6 =
1678 severity_distance_VC + severity_distance_VI + severity_distance_VA
1679 + severity_distance_CR + severity_distance_IR + severity_distance_AR;
1680 *current_severity_distance_eq4 =
1681 severity_distance_SC + severity_distance_SI + severity_distance_SA;
1682 *current_severity_distance_eq5 = 0.0;
1683}
1684
1696static void
1697cvss4_max_severities (const char *macrovector, double *max_severity_eq1,
1698 double *max_severity_eq2, double *max_severity_eq3eq6,
1699 double *max_severity_eq4)
1700{
1701 switch (macrovector[0])
1702 {
1703 case '0':
1704 *max_severity_eq1 = 0.1;
1705 break;
1706 case '1':
1707 *max_severity_eq1 = 0.4;
1708 break;
1709 case '2':
1710 *max_severity_eq1 = 0.5;
1711 break;
1712 default:
1713 *max_severity_eq1 = -99.0;
1714 }
1715
1716 switch (macrovector[1])
1717 {
1718 case '0':
1719 *max_severity_eq2 = 0.1;
1720 break;
1721 case '1':
1722 *max_severity_eq2 = 0.2;
1723 break;
1724 default:
1725 *max_severity_eq2 = -99.0;
1726 }
1727
1728 switch (macrovector[2])
1729 {
1730 case '0':
1731 if (macrovector[5] == '0')
1732 *max_severity_eq3eq6 = 0.7;
1733 else
1734 *max_severity_eq3eq6 = 0.6;
1735 break;
1736 case '1':
1737 *max_severity_eq3eq6 = 0.8;
1738 break;
1739 case '2':
1740 *max_severity_eq3eq6 = 1.0;
1741 break;
1742 default:
1743 *max_severity_eq3eq6 = -99.0;
1744 }
1745
1746 switch (macrovector[3])
1747 {
1748 case '0':
1749 *max_severity_eq4 = 0.6;
1750 break;
1751 case '1':
1752 *max_severity_eq4 = 0.5;
1753 break;
1754 case '2':
1755 *max_severity_eq4 = 0.4;
1756 break;
1757 default:
1758 *max_severity_eq4 = -99.0;
1759 }
1760}
1761
1769static double
1771{
1772 char *vec = NULL;
1773 char *macrovector = NULL;
1774
1775 double available_distance_eq1, available_distance_eq2;
1776 double available_distance_eq3eq6;
1777 double available_distance_eq4, available_distance_eq5;
1778
1779 double current_severity_distance_eq1, current_severity_distance_eq2;
1780 double current_severity_distance_eq3eq6;
1781 double current_severity_distance_eq4, current_severity_distance_eq5;
1782
1783 double max_severity_eq1, max_severity_eq2, max_severity_eq3eq6;
1784 double max_severity_eq4;
1785
1786 double mean_distance, value;
1787
1788 int n_existing_lower = 0;
1789
1790 // Convert vector to simplified, enum-indexed string
1791 g_debug ("%s: CVSS string: %s", __func__, cvss_str);
1792 vec = simplify_cvss4_vector (cvss_str);
1793 g_debug ("%s: simplified vector: %s", __func__, vec);
1794 if (vec == NULL)
1795 return -1.0;
1796
1797 // Calculate macrovector
1798 macrovector = cvss4_macrovector (vec);
1799 value = cvss4_macrovector_score (macrovector);
1800 g_debug ("%s: macrovector: %s, value: %0.1f", __func__, macrovector, value);
1801 if (macrovector == NULL || value == -1.0)
1802 {
1803 g_free (vec);
1804 return -1.0;
1805 }
1806
1807 // Calculate maximum distances
1809 macrovector, &available_distance_eq1, &available_distance_eq2,
1810 &available_distance_eq3eq6, &available_distance_eq4,
1811 &available_distance_eq5);
1812 g_debug ("%s: maximal scoring diffs:"
1813 " EQ1:%0.1f EQ2:%0.1f EQ3+EQ6:%0.1f EQ5:%0.1f EQ6:%0.1f",
1814 __func__, available_distance_eq1, available_distance_eq2,
1815 available_distance_eq3eq6, available_distance_eq4,
1816 available_distance_eq5);
1817
1818 // Calculate current severity distances
1820 vec, macrovector, &current_severity_distance_eq1,
1821 &current_severity_distance_eq2, &current_severity_distance_eq3eq6,
1822 &current_severity_distance_eq4, &current_severity_distance_eq5);
1823
1824 g_debug ("%s: current severity distances:"
1825 "EQ1:%0.1f EQ2:%0.1f EQ3+EQ6:%0.1f EQ4:%0.1f EQ5:%0.1f",
1826 __func__, current_severity_distance_eq1,
1827 current_severity_distance_eq2, current_severity_distance_eq3eq6,
1828 current_severity_distance_eq4, current_severity_distance_eq5);
1829
1830 // Get MaxSeverity
1831 cvss4_max_severities (macrovector, &max_severity_eq1, &max_severity_eq2,
1832 &max_severity_eq3eq6, &max_severity_eq4);
1833
1834 g_free (vec);
1835 g_free (macrovector);
1836
1837 // Calculate mean distances
1838 mean_distance = 0.0;
1839 if (available_distance_eq1 >= 0.0)
1840 {
1841 n_existing_lower++;
1842 double percent_to_next_severity =
1843 (current_severity_distance_eq1) / max_severity_eq1;
1844 mean_distance += (available_distance_eq1 * percent_to_next_severity);
1845 }
1846
1847 if (available_distance_eq2 >= 0.0)
1848 {
1849 n_existing_lower++;
1850 double percent_to_next_severity =
1851 (current_severity_distance_eq2) / max_severity_eq2;
1852 mean_distance += (available_distance_eq2 * percent_to_next_severity);
1853 }
1854
1855 if (available_distance_eq3eq6 >= 0.0)
1856 {
1857 n_existing_lower++;
1858 double percent_to_next_severity =
1859 (current_severity_distance_eq3eq6) / max_severity_eq3eq6;
1860 mean_distance += (available_distance_eq3eq6 * percent_to_next_severity);
1861 }
1862
1863 if (available_distance_eq4 >= 0.0)
1864 {
1865 n_existing_lower++;
1866 double percent_to_next_severity =
1867 (current_severity_distance_eq4) / max_severity_eq4;
1868 mean_distance += (available_distance_eq4 * percent_to_next_severity);
1869 }
1870
1871 if (available_distance_eq5 >= 0.0)
1872 {
1873 // For EQ5 the percentage is always 0
1874 n_existing_lower++;
1875 }
1876
1877 mean_distance = mean_distance / n_existing_lower;
1878
1879 // Get and adjust macrovector score
1880 value = value - mean_distance;
1881 if (value < 0.0)
1882 value = 0.0;
1883 else if (value > 10.0)
1884 value = 10.0;
1885
1886 return round (value * 10.0) / 10.0;
1887}
static void cvss4_current_severity_distances(const char *vec, const char *macrovector, double *current_severity_distance_eq1, double *current_severity_distance_eq2, double *current_severity_distance_eq3eq6, double *current_severity_distance_eq4, double *current_severity_distance_eq5)
Calculate current severity distances for given CVSS 4.0 vector.
Definition cvss.c:1604
#define C_COMPLETE
Definition cvss.c:122
cvss4_metric_t
CVSS 4.0 metrics.
Definition cvss.c:219
@ CVSS4_PR
Definition cvss.c:224
@ CVSS4_VA
Definition cvss.c:228
@ CVSS4_V
Definition cvss.c:253
@ CVSS4_AV
Definition cvss.c:221
@ CVSS4_MSA
Definition cvss.c:248
@ CVSS4_MAC
Definition cvss.c:239
@ CVSS4_MSC
Definition cvss.c:246
@ CVSS4_E
Definition cvss.c:233
@ CVSS4_RE
Definition cvss.c:254
@ CVSS4_U
Definition cvss.c:255
@ CVSS4_MAV
Definition cvss.c:238
@ CVSS4_AR
Definition cvss.c:237
@ CVSS4_R
Definition cvss.c:252
@ CVSS4_SA
Definition cvss.c:231
@ CVSS4_S
Definition cvss.c:250
@ CVSS4_VI
Definition cvss.c:227
@ CVSS4_MPR
Definition cvss.c:241
@ CVSS4_UI
Definition cvss.c:225
@ CVSS4_VC
Definition cvss.c:226
@ CVSS4_SC
Definition cvss.c:229
@ CVSS4_AU
Definition cvss.c:251
@ CVSS4_MSI
Definition cvss.c:247
@ CVSS4_MVC
Definition cvss.c:243
@ CVSS4_MUI
Definition cvss.c:242
@ CVSS4_MAT
Definition cvss.c:240
@ CVSS4_AT
Definition cvss.c:223
@ CVSS4_AC
Definition cvss.c:222
@ CVSS4_CR
Definition cvss.c:235
@ CVSS4_MVI
Definition cvss.c:244
@ CVSS4_METRICS_MAX
Definition cvss.c:257
@ CVSS4_MVA
Definition cvss.c:245
@ CVSS4_SI
Definition cvss.c:230
@ CVSS4_IR
Definition cvss.c:236
static void cvss4_init_macrovector_table()
Initialize the CVSS 4.0 macrovector lookup table.
Definition cvss.c:840
static int set_impact_from_str(const char *value, enum base_metrics metric, struct cvss *cvss)
Set impact score from string representation.
Definition cvss.c:504
#define AV_LOCAL
Definition cvss.c:101
static double get_exploitability_subscore(const struct cvss *cvss)
Calculate Exploitability Sub Score.
Definition cvss.c:488
#define Au_SINGLE_INSTANCE
Definition cvss.c:114
static double cvss4_macrovector_score(const char *vector)
Get the CVSS 4.0 score for a given macrovector string.
Definition cvss.c:865
#define A_PARTIAL
Definition cvss.c:135
#define CVSS_MACROVECTOR_BLANK
Blank simplified CVSS 4.0 macrovector string.
Definition cvss.c:268
static int toenum(const char *str, enum base_metrics *res)
Determine base metric enumeration from a string.
Definition cvss.c:438
#define C_NONE
ConfidentialityImpact (C) Constants.
Definition cvss.c:120
#define I_PARTIAL
Definition cvss.c:128
static cvss4_metric_def_t cvss4_metric_defs[]
String to enum mappings and allowed values for CVSS 4.0 metrics.
Definition cvss.c:294
static double v3_impact(const char *value)
Get impact.
Definition cvss.c:677
#define AV_NETWORK
AccessVector (AV) Constants.
Definition cvss.c:99
static const cvss4_macrovector_mapping_t cvss4_macrovector_mappings[]
CVSS 4.0 macrovector mappings.
Definition cvss.c:350
static GHashTable * cvss4_macrovector_table
Hashtable for quick lookup of CVSS macrovector scores.
Definition cvss.c:427
#define C_PARTIAL
Definition cvss.c:121
static char cvss4_m(const char *simplified_vec, cvss4_metric_t metric)
Get the effective value of a metric in a simplified CVSS4 vector.
Definition cvss.c:888
#define CVSS_METRICS_STR_BLANK
Blank simplified CVSS 4.0 metrics string.
Definition cvss.c:263
#define A_COMPLETE
Definition cvss.c:136
static gchar * cvss4_macrovector(const char *vec)
Calculate CVSS 4.0 macrovector from a simplified vector.
Definition cvss.c:1098
static gchar * simplify_cvss4_vector(const char *cvss_str)
Simplify CVSS 4.0 base vector so metrics can be indexed by enum.
Definition cvss.c:929
static gchar * cvss4_vector_expand(const char *vec)
Expands a simplified CVSS 4.0 vector into its full string form.
Definition cvss.c:1049
#define Au_MULTIPLE_INSTANCES
Authentication (Au) Constants.
Definition cvss.c:113
static double get_impact_subscore(const struct cvss *cvss)
Calculate Impact Sub Score.
Definition cvss.c:471
#define AC_LOW
AccessComplexity (AC) Constants.
Definition cvss.c:106
#define I_NONE
IntegrityImpact (I) Constants.
Definition cvss.c:127
static double get_cvss_score_from_metrics_v4(const char *)
Calculate CVSS 4.0 Score.
Definition cvss.c:1770
static const struct impact_item impact_map[][3]
Definition cvss.c:174
static double cvss4_metric_level(cvss4_metric_t metric, char value)
Get the index of a CVSS 4.0 metric value for severity distances.
Definition cvss.c:1443
base_metrics
CVSS v2 Base metrics.
Definition cvss.c:143
@ AC
Definition cvss.c:148
@ A
Definition cvss.c:144
@ C
Definition cvss.c:146
@ Au
Definition cvss.c:147
@ I
Definition cvss.c:145
@ AV
Definition cvss.c:149
static double get_cvss_score_from_base_metrics_v3(const char *)
Calculate CVSS Score.
Definition cvss.c:696
#define A_NONE
AvailabilityImpact (A) Constants.
Definition cvss.c:134
double get_cvss_score_from_base_metrics(const char *cvss_str)
Calculate CVSS Score.
Definition cvss.c:585
static void cvss4_max_severities(const char *macrovector, double *max_severity_eq1, double *max_severity_eq2, double *max_severity_eq3eq6, double *max_severity_eq4)
Get the max severity values for a CVSS 4.0 macrovector.
Definition cvss.c:1697
static double __get_cvss_score(struct cvss *cvss)
Final CVSS score computation helper.
Definition cvss.c:561
#define I_COMPLETE
Definition cvss.c:129
static double cvss4_severity_distance(cvss4_metric_t metric, const char *vec, const char *max_vec)
Calculate severity distance for a metric in two CVSS 4.0 vectors.
Definition cvss.c:1585
#define AC_MEDIUM
Definition cvss.c:107
#define AV_ADJACENT_NETWORK
Definition cvss.c:100
static double roundup(double cvss)
Round final score as in spec.
Definition cvss.c:650
static gchar ** cvss4_max_vectors(const char *macrovector)
Composes a list of max vectors for the given CVSS 4.0 macrovector.
Definition cvss.c:1332
#define AC_HIGH
Definition cvss.c:108
static void cvss4_maximal_scoring_differences(const char *macrovector, double *available_distance_eq1, double *available_distance_eq2, double *available_distance_eq3eq6, double *available_distance_eq4, double *available_distance_eq5)
Calulate the maximal scoring differences from a CVSS 4.0 macrovector.
Definition cvss.c:1196
#define Au_NONE
Definition cvss.c:115
Protos for CVSS utility functions.
String utilities.
Key-Value mappings of CVSS 4.0 macrovectors to scores.
Definition cvss.c:338
const char * vector
Definition cvss.c:339
double score
Definition cvss.c:340
String to enum mapping and allowed values for a CVSS 4.0 metric.
Definition cvss.c:278
const char * metric_str
Definition cvss.c:279
const char * values
Definition cvss.c:281
const cvss4_metric_t metric
Definition cvss.c:280
Describe a CVSS metrics.
Definition cvss.c:165
double integ_impact
Definition cvss.c:167
double access_vector
Definition cvss.c:169
double avail_impact
Definition cvss.c:168
double authentication
Definition cvss.c:171
double access_complexity
Definition cvss.c:170
double conf_impact
Definition cvss.c:166
Describe a CVSS impact element.
Definition cvss.c:156
const char * name
Definition cvss.c:157
double nvalue
Definition cvss.c:158