Greenbone Vulnerability Management Libraries 22.18.1
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 break;
960 }
961 else if (strcasecmp (metric_str, "U") == 0)
962 {
963 // Special case for the Provider Urgency metric
964 if (strcasecmp (value, "Red") && strcasecmp (value, "Amber")
965 && strcasecmp (value, "Green") && strcasecmp (value, "Clear")
966 && strcasecmp (value, "X"))
967 {
968 g_debug ("%s: value for metric %s must be one of"
969 " 'Red', 'Amber', 'Green', 'Clear', 'X'",
970 __func__, metric_str);
971 break;
972 }
973 else
974 valid = TRUE;
975 }
976 else if (strlen (value) != 1)
977 {
978 g_debug ("%s: value for metric %s must be 1 character", __func__,
979 metric_str);
980 break;
981 }
982
983 cvss4_metric_def_t *metric_def = &cvss4_metric_defs[0];
984 while (metric_def->metric_str)
985 {
986 if (strcasecmp (metric_str, metric_def->metric_str) == 0)
987 {
988 char value_char = g_ascii_toupper (value[0]);
989
990 // Reject duplicate metrics
991 if (vec[metric_def->metric] != 'X')
992 {
993 g_debug ("%s: duplicate metric %s", __func__, metric_str);
994 break;
995 }
996
997 // Set the metric in the simplified vector
998 if (strchr (metric_def->values, value_char))
999 {
1000 valid = TRUE;
1001 vec[metric_def->metric] = value_char;
1002 }
1003 else
1004 {
1005 g_debug ("%s: invalid metric: %s:%c", __func__, metric_str,
1006 value_char);
1007 }
1008 break;
1009 }
1010 metric_def++;
1011 }
1012
1013 split_cvss_point++;
1014 g_strfreev (split_component);
1015 }
1016 g_strfreev (split_cvss_str);
1017
1018 for (metric = CVSS4_AV; valid && metric <= CVSS4_SA; metric++)
1019 {
1020 if (vec[metric] == 'X')
1021 {
1022 g_debug ("%s: mandatory metric %s is undefined", __func__,
1023 cvss4_metric_defs[metric].metric_str);
1024 valid = FALSE;
1025 }
1026 }
1027
1028 if (!valid)
1029 {
1030 g_debug ("%s: vector %s is invalid", __func__, cvss_str);
1031 g_free (vec);
1032 return NULL;
1033 }
1034
1035 return vec;
1036}
1037
1045static gchar *
1046cvss4_vector_expand (const char *vec)
1047{
1048 cvss4_metric_t metric;
1049 GString *str = g_string_new ("CVSS:4.0");
1050 for (metric = 0; metric < CVSS4_METRICS_MAX; metric++)
1051 {
1052 const char *expanded_value;
1053 if (vec[metric] == 'X')
1054 continue;
1056 if (metric == CVSS4_U)
1057 {
1058 switch (vec[metric])
1059 {
1060 case 'R':
1061 expanded_value = "Red";
1062 break;
1063 case 'A':
1064 expanded_value = "Amber";
1065 break;
1066 case 'G':
1067 expanded_value = "Green";
1068 break;
1069 case 'C':
1070 expanded_value = "Clear";
1071 break;
1072 default:
1073 expanded_value = NULL;
1074 }
1075 }
1076 else
1077 expanded_value = NULL;
1078
1079 if (expanded_value)
1080 g_string_append_printf (str, "/%s:%s", def.metric_str, expanded_value);
1081 else
1082 g_string_append_printf (str, "/%s:%c", def.metric_str, vec[metric]);
1083 }
1084 return g_string_free (str, FALSE);
1085}
1086
1094static inline gchar *
1095cvss4_macrovector (const char *vec)
1096{
1097 gchar *macrovector;
1098 if (vec == NULL)
1099 return NULL;
1100
1101 macrovector = g_strdup (CVSS_MACROVECTOR_BLANK);
1102
1103 // EQ1: 0-AV:N and PR:N and UI:N
1104 // 1-(AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P
1105 // 2-AV:P or not(AV:N or PR:N or UI:N)
1106 char av = cvss4_m (vec, CVSS4_AV);
1107 char pr = cvss4_m (vec, CVSS4_PR);
1108 char ui = cvss4_m (vec, CVSS4_UI);
1109
1110 if (av == 'N' && pr == 'N' && ui == 'N')
1111 macrovector[0] = '0';
1112 else if ((av == 'N' || pr == 'N' || ui == 'N') && !(av == 'P'))
1113 macrovector[0] = '1';
1114 else
1115 macrovector[0] = '2';
1116
1117 // EQ2: 0-(AC:L and AT:N)
1118 // 1-(not(AC:L and AT:N))
1119 char ac = cvss4_m (vec, CVSS4_AC);
1120 char at = cvss4_m (vec, CVSS4_AT);
1121
1122 if (ac == 'L' && at == 'N')
1123 macrovector[1] = '0';
1124 else
1125 macrovector[1] = '1';
1126
1127 // EQ3: 0-(VC:H and VI:H)
1128 // 1-(not(VC:H and VI:H) and (VC:H or VI:H or VA:H))
1129 // 2-not (VC:H or VI:H or VA:H)
1130 char vc = cvss4_m (vec, CVSS4_VC);
1131 char vi = cvss4_m (vec, CVSS4_VI);
1132 char va = cvss4_m (vec, CVSS4_VA);
1133
1134 if (vc == 'H' && vi == 'H')
1135 macrovector[2] = '0';
1136 else if (vc == 'H' || vi == 'H' || va == 'H')
1137 macrovector[2] = '1';
1138 else
1139 macrovector[2] = '2';
1140
1141 // EQ4: 0-(MSI:S or MSA:S)
1142 // 1-not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H)
1143 // 2-not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H)
1144 //
1145 // "Effective" SI and SA are the same as MSI and MSA for the purposes of
1146 // checking for the "Safety" value.
1147 char sc = cvss4_m (vec, CVSS4_SC);
1148 char si = cvss4_m (vec, CVSS4_SI);
1149 char sa = cvss4_m (vec, CVSS4_SA);
1150 if (si == 'S' || sa == 'S')
1151 macrovector[3] = '0';
1152 else if (sc == 'H' || si == 'H' || sa == 'H')
1153 macrovector[3] = '1';
1154 else
1155 macrovector[3] = '2';
1156
1157 // EQ5: 0-E:A
1158 // 1-E:P
1159 // 2-E:U
1160 char e = cvss4_m (vec, CVSS4_E);
1161 if (e == 'A')
1162 macrovector[4] = '0';
1163 else if (e == 'P')
1164 macrovector[4] = '1';
1165 else
1166 macrovector[4] = '2';
1167
1168 // EQ6: 0-(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)
1169 // 1-not[(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)]
1170 char cr = cvss4_m (vec, CVSS4_CR);
1171 char ir = cvss4_m (vec, CVSS4_IR);
1172 char ar = cvss4_m (vec, CVSS4_AR);
1173 if ((cr == 'H' && vc == 'H') || (ir == 'H' && vi == 'H')
1174 || (ar == 'H' && va == 'H'))
1175 macrovector[5] = '0';
1176 else
1177 macrovector[5] = '1';
1178
1179 return macrovector;
1180}
1181
1192static void
1193cvss4_maximal_scoring_differences (const char *macrovector,
1194 double *available_distance_eq1,
1195 double *available_distance_eq2,
1196 double *available_distance_eq3eq6,
1197 double *available_distance_eq4,
1198 double *available_distance_eq5)
1199{
1200 double value = cvss4_macrovector_score (macrovector);
1201 double score_eq1_next_lower_macro, score_eq2_next_lower_macro;
1202 double score_eq3eq6_next_lower_macro;
1203 double score_eq4_next_lower_macro, score_eq5_next_lower_macro;
1204
1205 // Next lower macrovector for EQ1 only exists if EQ1 is 0 or 1.
1206 if (macrovector[0] <= '1')
1207 {
1208 gchar *eq1_next_lower_macro = g_strdup (macrovector);
1209 eq1_next_lower_macro[0]++;
1210 score_eq1_next_lower_macro =
1211 cvss4_macrovector_score (eq1_next_lower_macro);
1212 g_free (eq1_next_lower_macro);
1213 }
1214 else
1215 score_eq1_next_lower_macro = -1.0;
1216
1217 // Next lower macrovector for EQ2 only exists if EQ2 is 0.
1218 if (macrovector[1] == '0')
1219 {
1220 gchar *eq2_next_lower_macro = g_strdup (macrovector);
1221 eq2_next_lower_macro[1]++;
1222 score_eq2_next_lower_macro =
1223 cvss4_macrovector_score (eq2_next_lower_macro);
1224 }
1225 else
1226 score_eq2_next_lower_macro = -1.0;
1227
1228 // Next lower macrovector for EQ3.
1229 if ((macrovector[2] == '0' || macrovector[2] == '1') && macrovector[5] == '1')
1230 {
1231 gchar *eq3eq6_next_lower_macro = g_strdup (macrovector);
1232 eq3eq6_next_lower_macro[2]++;
1233 score_eq3eq6_next_lower_macro =
1234 cvss4_macrovector_score (eq3eq6_next_lower_macro);
1235 g_free (eq3eq6_next_lower_macro);
1236 }
1237 else if (macrovector[2] == '1' && macrovector[5] == '0')
1238 {
1239 gchar *eq3eq6_next_lower_macro = g_strdup (macrovector);
1240 eq3eq6_next_lower_macro[5]++;
1241 score_eq3eq6_next_lower_macro =
1242 cvss4_macrovector_score (eq3eq6_next_lower_macro);
1243 g_free (eq3eq6_next_lower_macro);
1244 }
1245 else if (macrovector[2] == '0' && macrovector[5] == '0')
1246 {
1247 gchar *eq3eq6_next_lower_macro_left = g_strdup (macrovector);
1248 eq3eq6_next_lower_macro_left[5]++;
1249 gchar *eq3eq6_next_lower_macro_right = g_strdup (macrovector);
1250 eq3eq6_next_lower_macro_right[2]++;
1251 double score_eq3eq6_next_lower_macro_left =
1252 cvss4_macrovector_score (eq3eq6_next_lower_macro_left);
1253 double score_eq3eq6_next_lower_macro_right =
1254 cvss4_macrovector_score (eq3eq6_next_lower_macro_right);
1255
1256 if (score_eq3eq6_next_lower_macro_left
1257 > score_eq3eq6_next_lower_macro_right)
1258 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_left;
1259 else
1260 score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_right;
1261
1262 g_free (eq3eq6_next_lower_macro_left);
1263 g_free (eq3eq6_next_lower_macro_right);
1264 }
1265 else
1266 score_eq3eq6_next_lower_macro = -1.0;
1267
1268 // Next lower macrovector for EQ4 only exists if EQ4 is 0 or 1.
1269 if (macrovector[3] <= '1')
1270 {
1271 gchar *eq4_next_lower_macro = g_strdup (macrovector);
1272 eq4_next_lower_macro[3]++;
1273 score_eq4_next_lower_macro =
1274 cvss4_macrovector_score (eq4_next_lower_macro);
1275 g_free (eq4_next_lower_macro);
1276 }
1277 else
1278 score_eq4_next_lower_macro = -1.0;
1279
1280 // Next lower macrovector for EQ5 only exists if EQ5 is 0 or 1.
1281 if (macrovector[4] <= '1')
1282 {
1283 gchar *eq5_next_lower_macro = g_strdup (macrovector);
1284 eq5_next_lower_macro[4]++;
1285 score_eq5_next_lower_macro =
1286 cvss4_macrovector_score (eq5_next_lower_macro);
1287 g_free (eq5_next_lower_macro);
1288 }
1289 else
1290 score_eq5_next_lower_macro = -1.0;
1291
1292 if (value != -1.0)
1293 {
1294 *available_distance_eq1 = score_eq1_next_lower_macro != -1.0
1295 ? value - score_eq1_next_lower_macro
1296 : -1.0;
1297 *available_distance_eq2 = score_eq2_next_lower_macro != -1.0
1298 ? value - score_eq2_next_lower_macro
1299 : -1.0;
1300 *available_distance_eq3eq6 = score_eq3eq6_next_lower_macro != -1.0
1301 ? value - score_eq3eq6_next_lower_macro
1302 : -1.0;
1303 *available_distance_eq4 = score_eq4_next_lower_macro != -1.0
1304 ? value - score_eq4_next_lower_macro
1305 : -1.0;
1306 *available_distance_eq5 = score_eq5_next_lower_macro != -1.0
1307 ? value - score_eq5_next_lower_macro
1308 : -1.0;
1309 }
1310 else
1311 {
1312 *available_distance_eq1 = -1.0;
1313 *available_distance_eq2 = -1.0;
1314 *available_distance_eq3eq6 = -1.0;
1315 *available_distance_eq4 = -1.0;
1316 *available_distance_eq5 = -1.0;
1317 }
1318}
1319
1327static gchar **
1328cvss4_max_vectors (const char *macrovector)
1329{
1330 const char **eq1_maxes, **eq2_maxes, **eq3eq6_maxes;
1331 const char *eq4_max, *eq5_max;
1332 gchar **ret;
1333
1334 // EQ1
1335 static const char *eq1_maxes_0[] = {"AV:N/PR:N/UI:N/", NULL};
1336 static const char *eq1_maxes_1[] = {"AV:A/PR:N/UI:N/", "AV:N/PR:L/UI:N/",
1337 "AV:N/PR:N/UI:P/", NULL};
1338 static const char *eq1_maxes_2[] = {"AV:P/PR:N/UI:N/", "AV:A/PR:L/UI:P/",
1339 NULL};
1340 if (macrovector[0] == '0')
1341 eq1_maxes = eq1_maxes_0;
1342 else if (macrovector[0] == '1')
1343 eq1_maxes = eq1_maxes_1;
1344 else
1345 eq1_maxes = eq1_maxes_2;
1346
1347 // EQ2
1348 static const char *eq2_maxes_0[] = {"AC:L/AT:N/", NULL};
1349 static const char *eq2_maxes_1[] = {"AC:H/AT:N/", "AC:L/AT:P/", NULL};
1350 if (macrovector[1] == '0')
1351 eq2_maxes = eq2_maxes_0;
1352 else
1353 eq2_maxes = eq2_maxes_1;
1354
1355 // EQ3+EQ6
1356 static const char *eq3eq6_maxes_00[] = {"VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/",
1357 NULL};
1358 static const char *eq3eq6_maxes_01[] = {
1359 "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};
1360 static const char *eq3eq6_maxes_10[] = {
1361 "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};
1362 static const char *eq3eq6_maxes_11[] = {
1363 "VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/", "VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/",
1364 "VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/", "VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/",
1365 "VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/", NULL};
1366 static const char *eq3eq6_maxes_21[] = {"VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/",
1367 NULL};
1368 if ((macrovector[2] == '0'))
1369 {
1370 if (macrovector[5] == '0')
1371 eq3eq6_maxes = eq3eq6_maxes_00;
1372 else
1373 eq3eq6_maxes = eq3eq6_maxes_01;
1374 }
1375 else if ((macrovector[2] == '1'))
1376 {
1377 if (macrovector[5] == '0')
1378 eq3eq6_maxes = eq3eq6_maxes_10;
1379 else
1380 eq3eq6_maxes = eq3eq6_maxes_11;
1381 }
1382 else
1383 eq3eq6_maxes = eq3eq6_maxes_21;
1384
1385 // EQ4
1386 if (macrovector[3] == '0')
1387 eq4_max = "SC:H/SI:S/SA:S/";
1388 else if (macrovector[3] == '1')
1389 eq4_max = "SC:H/SI:H/SA:H/";
1390 else
1391 eq4_max = "SC:L/SI:L/SA:L/";
1392
1393 // EQ5
1394 if (macrovector[4] == '0')
1395 eq5_max = "E:A/";
1396 else if (macrovector[4] == '1')
1397 eq5_max = "E:P/";
1398 else
1399 eq5_max = "E:U/";
1400
1401 GPtrArray *max_vectors = g_ptr_array_new ();
1402 const char **eq1_max, **eq2_max, **eq3eq6_max;
1403 for (eq1_max = eq1_maxes; *eq1_max != NULL; eq1_max++)
1404 {
1405 for (eq2_max = eq2_maxes; *eq2_max != NULL; eq2_max++)
1406 {
1407 for (eq3eq6_max = eq3eq6_maxes; *eq3eq6_max != NULL; eq3eq6_max++)
1408 {
1409 gchar *full_vector =
1410 g_strdup_printf ("%s%s%s%s%s", *eq1_max, *eq2_max, *eq3eq6_max,
1411 eq4_max, eq5_max);
1412 gchar *vector = simplify_cvss4_vector (full_vector);
1413 if (vector == NULL)
1414 g_warning ("%s: generated vector %s is invalid", __func__,
1415 full_vector);
1416 else
1417 g_ptr_array_add (max_vectors, vector);
1418 g_free (full_vector);
1419 }
1420 }
1421 }
1422
1423 g_ptr_array_add (max_vectors, NULL);
1424
1425 ret = (gchar **) max_vectors->pdata;
1426 g_ptr_array_free (max_vectors, FALSE);
1427 return ret;
1428}
1429
1438static double
1440{
1441 switch (metric)
1442 {
1443 case CVSS4_AV:
1444 switch (value)
1445 {
1446 case 'N':
1447 return 0.0;
1448 case 'A':
1449 return 0.1;
1450 case 'L':
1451 return 0.2;
1452 case 'P':
1453 return 0.3;
1454 default:
1455 return -99.0;
1456 }
1457 break;
1458 case CVSS4_PR:
1459 switch (value)
1460 {
1461 case 'N':
1462 return 0.0;
1463 case 'L':
1464 return 0.1;
1465 case 'H':
1466 return 0.2;
1467 default:
1468 return -99.0;
1469 }
1470 break;
1471 case CVSS4_UI:
1472 switch (value)
1473 {
1474 case 'N':
1475 return 0.0;
1476 case 'P':
1477 return 0.1;
1478 case 'A':
1479 return 0.2;
1480 default:
1481 return -99.0;
1482 }
1483 break;
1484 case CVSS4_AC:
1485 switch (value)
1486 {
1487 case 'L':
1488 return 0.0;
1489 case 'H':
1490 return 0.1;
1491 default:
1492 return -99.0;
1493 }
1494 break;
1495 case CVSS4_AT:
1496 switch (value)
1497 {
1498 case 'N':
1499 return 0.0;
1500 case 'P':
1501 return 0.1;
1502 default:
1503 return -99.0;
1504 }
1505 break;
1506 case CVSS4_VC:
1507 case CVSS4_VI:
1508 case CVSS4_VA:
1509 switch (value)
1510 {
1511 case 'H':
1512 return 0.0;
1513 case 'L':
1514 return 0.1;
1515 case 'N':
1516 return 0.2;
1517 default:
1518 return -99.0;
1519 }
1520 break;
1521 case CVSS4_SC:
1522 case CVSS4_SI:
1523 case CVSS4_SA:
1524 switch (value)
1525 {
1526 case 'S':
1527 return 0.0;
1528 case 'H':
1529 return 0.1;
1530 case 'L':
1531 return 0.2;
1532 case 'N':
1533 return 0.3;
1534 default:
1535 return -99.0;
1536 }
1537 break;
1538 case CVSS4_CR:
1539 case CVSS4_IR:
1540 case CVSS4_AR:
1541 switch (value)
1542 {
1543 case 'H':
1544 return 0.0;
1545 case 'M':
1546 return 0.1;
1547 case 'L':
1548 return 0.2;
1549 default:
1550 return -99.0;
1551 }
1552 break;
1553
1554 // The Exploit Maturity metric is included in the reference implementation
1555 // but never used
1556 /*
1557 case CVSS4_E:
1558 switch (value)
1559 {
1560 case 'A': return 0.0;
1561 case 'P': return 0.1;
1562 case 'U': return 0.2;
1563 }
1564 break;
1565 */
1566 default:
1567 return -99.0;
1568 }
1569}
1570
1580static inline double
1582 const char *max_vec)
1583{
1584 return cvss4_metric_level (metric, cvss4_m (vec, metric))
1585 - cvss4_metric_level (metric, max_vec[metric]);
1586}
1587
1599static void
1600cvss4_current_severity_distances (const char *vec, const char *macrovector,
1601 double *current_severity_distance_eq1,
1602 double *current_severity_distance_eq2,
1603 double *current_severity_distance_eq3eq6,
1604 double *current_severity_distance_eq4,
1605 double *current_severity_distance_eq5)
1606{
1607 double severity_distance_AV, severity_distance_PR, severity_distance_UI;
1608 double severity_distance_AC, severity_distance_AT;
1609 double severity_distance_VC, severity_distance_VI, severity_distance_VA;
1610 double severity_distance_SC, severity_distance_SI, severity_distance_SA;
1611 double severity_distance_CR, severity_distance_IR, severity_distance_AR;
1612
1613 severity_distance_AV = severity_distance_PR = severity_distance_UI = -99.0;
1614 severity_distance_AC = severity_distance_AT = -99.0;
1615 severity_distance_VC = severity_distance_VI = severity_distance_VA = -99.0;
1616 severity_distance_SC = severity_distance_SI = severity_distance_SA = -99.0;
1617 severity_distance_CR = severity_distance_IR = severity_distance_AR = -99.0;
1618
1619 char **max_vectors, **max_vec;
1620 max_vectors = cvss4_max_vectors (macrovector);
1621 for (max_vec = max_vectors; *max_vec != NULL; max_vec++)
1622 {
1623 severity_distance_AV = cvss4_severity_distance (CVSS4_AV, vec, *max_vec);
1624 severity_distance_PR = cvss4_severity_distance (CVSS4_PR, vec, *max_vec);
1625 severity_distance_UI = cvss4_severity_distance (CVSS4_UI, vec, *max_vec);
1626
1627 severity_distance_AC = cvss4_severity_distance (CVSS4_AC, vec, *max_vec);
1628 severity_distance_AT = cvss4_severity_distance (CVSS4_AT, vec, *max_vec);
1629
1630 severity_distance_VC = cvss4_severity_distance (CVSS4_VC, vec, *max_vec);
1631 severity_distance_VI = cvss4_severity_distance (CVSS4_VI, vec, *max_vec);
1632 severity_distance_VA = cvss4_severity_distance (CVSS4_VA, vec, *max_vec);
1633
1634 severity_distance_SC = cvss4_severity_distance (CVSS4_SC, vec, *max_vec);
1635 severity_distance_SI = cvss4_severity_distance (CVSS4_SI, vec, *max_vec);
1636 severity_distance_SA = cvss4_severity_distance (CVSS4_SA, vec, *max_vec);
1637
1638 severity_distance_CR = cvss4_severity_distance (CVSS4_CR, vec, *max_vec);
1639 severity_distance_IR = cvss4_severity_distance (CVSS4_IR, vec, *max_vec);
1640 severity_distance_AR = cvss4_severity_distance (CVSS4_AR, vec, *max_vec);
1641
1642 if (severity_distance_AV < 0.0 || severity_distance_PR < 0.0
1643 || severity_distance_UI < 0.0 || severity_distance_AC < 0.0
1644 || severity_distance_AT < 0.0 || severity_distance_VC < 0.0
1645 || severity_distance_VI < 0.0 || severity_distance_VA < 0.0
1646 || severity_distance_SC < 0.0 || severity_distance_SI < 0.0
1647 || severity_distance_SA < 0.0 || severity_distance_CR < 0.0
1648 || severity_distance_IR < 0.0 || severity_distance_AR < 0.0)
1649 continue;
1650
1651 g_debug ("%s AV:%0.1f PR:%0.1f UI:%0.1f |"
1652 " AC:%0.1f AT:%0.1f |"
1653 " VC:%0.1f VI:%0.1f VA:%0.1f |"
1654 " SC:%0.1f SI:%0.1f SA:%0.1f |"
1655 " CR:%0.1f IR:%0.1f AR:%0.1f",
1656 __func__, severity_distance_AV, severity_distance_PR,
1657 severity_distance_UI, severity_distance_AC, severity_distance_AT,
1658 severity_distance_VC, severity_distance_VI, severity_distance_VA,
1659 severity_distance_SC, severity_distance_SI, severity_distance_SA,
1660 severity_distance_CR, severity_distance_IR,
1661 severity_distance_AR);
1662 break;
1663 }
1664
1665 gchar *max_vec_expanded = cvss4_vector_expand (*max_vec);
1666 g_debug ("%s: max_vec: %s", __func__, max_vec_expanded);
1667 g_free (max_vec_expanded);
1668 g_strfreev (max_vectors);
1669
1670 *current_severity_distance_eq1 =
1671 severity_distance_AV + severity_distance_PR + severity_distance_UI;
1672 *current_severity_distance_eq2 = severity_distance_AC + severity_distance_AT;
1673 *current_severity_distance_eq3eq6 =
1674 severity_distance_VC + severity_distance_VI + severity_distance_VA
1675 + severity_distance_CR + severity_distance_IR + severity_distance_AR;
1676 *current_severity_distance_eq4 =
1677 severity_distance_SC + severity_distance_SI + severity_distance_SA;
1678 *current_severity_distance_eq5 = 0.0;
1679}
1680
1692static void
1693cvss4_max_severities (const char *macrovector, double *max_severity_eq1,
1694 double *max_severity_eq2, double *max_severity_eq3eq6,
1695 double *max_severity_eq4)
1696{
1697 switch (macrovector[0])
1698 {
1699 case '0':
1700 *max_severity_eq1 = 0.1;
1701 break;
1702 case '1':
1703 *max_severity_eq1 = 0.4;
1704 break;
1705 case '2':
1706 *max_severity_eq1 = 0.5;
1707 break;
1708 default:
1709 *max_severity_eq1 = -99.0;
1710 }
1711
1712 switch (macrovector[1])
1713 {
1714 case '0':
1715 *max_severity_eq2 = 0.1;
1716 break;
1717 case '1':
1718 *max_severity_eq2 = 0.2;
1719 break;
1720 default:
1721 *max_severity_eq2 = -99.0;
1722 }
1723
1724 switch (macrovector[2])
1725 {
1726 case '0':
1727 if (macrovector[5] == '0')
1728 *max_severity_eq3eq6 = 0.7;
1729 else
1730 *max_severity_eq3eq6 = 0.6;
1731 break;
1732 case '1':
1733 *max_severity_eq3eq6 = 0.8;
1734 break;
1735 case '2':
1736 *max_severity_eq3eq6 = 1.0;
1737 break;
1738 default:
1739 *max_severity_eq3eq6 = -99.0;
1740 }
1741
1742 switch (macrovector[3])
1743 {
1744 case '0':
1745 *max_severity_eq4 = 0.6;
1746 break;
1747 case '1':
1748 *max_severity_eq4 = 0.5;
1749 break;
1750 case '2':
1751 *max_severity_eq4 = 0.4;
1752 break;
1753 default:
1754 *max_severity_eq4 = -99.0;
1755 }
1756}
1757
1765static double
1767{
1768 char *vec = NULL;
1769 char *macrovector = NULL;
1770
1771 double available_distance_eq1, available_distance_eq2;
1772 double available_distance_eq3eq6;
1773 double available_distance_eq4, available_distance_eq5;
1774
1775 double current_severity_distance_eq1, current_severity_distance_eq2;
1776 double current_severity_distance_eq3eq6;
1777 double current_severity_distance_eq4, current_severity_distance_eq5;
1778
1779 double max_severity_eq1, max_severity_eq2, max_severity_eq3eq6;
1780 double max_severity_eq4;
1781
1782 double mean_distance, value;
1783
1784 int n_existing_lower = 0;
1785
1786 // Convert vector to simplified, enum-indexed string
1787 g_debug ("%s: CVSS string: %s", __func__, cvss_str);
1788 vec = simplify_cvss4_vector (cvss_str);
1789 g_debug ("%s: simplified vector: %s", __func__, vec);
1790 if (vec == NULL)
1791 return -1.0;
1792
1793 // Calculate macrovector
1794 macrovector = cvss4_macrovector (vec);
1795 value = cvss4_macrovector_score (macrovector);
1796 g_debug ("%s: macrovector: %s, value: %0.1f", __func__, macrovector, value);
1797 if (macrovector == NULL || value == -1.0)
1798 {
1799 g_free (vec);
1800 return -1.0;
1801 }
1802
1803 // Calculate maximum distances
1805 macrovector, &available_distance_eq1, &available_distance_eq2,
1806 &available_distance_eq3eq6, &available_distance_eq4,
1807 &available_distance_eq5);
1808 g_debug ("%s: maximal scoring diffs:"
1809 " EQ1:%0.1f EQ2:%0.1f EQ3+EQ6:%0.1f EQ5:%0.1f EQ6:%0.1f",
1810 __func__, available_distance_eq1, available_distance_eq2,
1811 available_distance_eq3eq6, available_distance_eq4,
1812 available_distance_eq5);
1813
1814 // Calculate current severity distances
1816 vec, macrovector, &current_severity_distance_eq1,
1817 &current_severity_distance_eq2, &current_severity_distance_eq3eq6,
1818 &current_severity_distance_eq4, &current_severity_distance_eq5);
1819
1820 g_debug ("%s: current severity distances:"
1821 "EQ1:%0.1f EQ2:%0.1f EQ3+EQ6:%0.1f EQ4:%0.1f EQ5:%0.1f",
1822 __func__, current_severity_distance_eq1,
1823 current_severity_distance_eq2, current_severity_distance_eq3eq6,
1824 current_severity_distance_eq4, current_severity_distance_eq5);
1825
1826 // Get MaxSeverity
1827 cvss4_max_severities (macrovector, &max_severity_eq1, &max_severity_eq2,
1828 &max_severity_eq3eq6, &max_severity_eq4);
1829
1830 g_free (vec);
1831 g_free (macrovector);
1832
1833 // Calculate mean distances
1834 mean_distance = 0.0;
1835 if (available_distance_eq1 >= 0.0)
1836 {
1837 n_existing_lower++;
1838 double percent_to_next_severity =
1839 (current_severity_distance_eq1) / max_severity_eq1;
1840 mean_distance += (available_distance_eq1 * percent_to_next_severity);
1841 }
1842
1843 if (available_distance_eq2 >= 0.0)
1844 {
1845 n_existing_lower++;
1846 double percent_to_next_severity =
1847 (current_severity_distance_eq2) / max_severity_eq2;
1848 mean_distance += (available_distance_eq2 * percent_to_next_severity);
1849 }
1850
1851 if (available_distance_eq3eq6 >= 0.0)
1852 {
1853 n_existing_lower++;
1854 double percent_to_next_severity =
1855 (current_severity_distance_eq3eq6) / max_severity_eq3eq6;
1856 mean_distance += (available_distance_eq3eq6 * percent_to_next_severity);
1857 }
1858
1859 if (available_distance_eq4 >= 0.0)
1860 {
1861 n_existing_lower++;
1862 double percent_to_next_severity =
1863 (current_severity_distance_eq4) / max_severity_eq4;
1864 mean_distance += (available_distance_eq4 * percent_to_next_severity);
1865 }
1866
1867 if (available_distance_eq5 >= 0.0)
1868 {
1869 // For EQ5 the percentage is always 0
1870 n_existing_lower++;
1871 }
1872
1873 mean_distance = mean_distance / n_existing_lower;
1874
1875 // Get and adjust macrovector score
1876 value = value - mean_distance;
1877 if (value < 0.0)
1878 value = 0.0;
1879 else if (value > 10.0)
1880 value = 10.0;
1881
1882 return round (value * 10.0) / 10.0;
1883}
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:1600
#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:1095
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:1046
#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:1766
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:1439
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:1693
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:1581
#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:1328
#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:1193
#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