Greenbone Vulnerability Management Libraries  22.8.0
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 
65 #include "cvss.h"
66 
67 #include <glib.h>
68 #include <math.h>
69 #include <strings.h>
70 
71 #undef G_LOG_DOMAIN
72 
75 #define G_LOG_DOMAIN "libgvm base"
76 
77 /* Static Headers. */
78 
79 static double
81 
82 /* CVSS v2. */
83 
84 // clang-format off
88 #define AV_NETWORK 1.0
89 #define AV_ADJACENT_NETWORK 0.646
90 #define AV_LOCAL 0.395
95 #define AC_LOW 0.71
96 #define AC_MEDIUM 0.61
97 #define AC_HIGH 0.35
102 #define Au_MULTIPLE_INSTANCES 0.45
103 #define Au_SINGLE_INSTANCE 0.56
104 #define Au_NONE 0.704
109 #define C_NONE 0.0
110 #define C_PARTIAL 0.275
111 #define C_COMPLETE 0.660
116 #define I_NONE 0.0
117 #define I_PARTIAL 0.275
118 #define I_COMPLETE 0.660
123 #define A_NONE 0.0
124 #define A_PARTIAL 0.275
125 #define A_COMPLETE 0.660
126 // clang-format on
127 
132 {
133  A,
134  I,
135  C,
136  Au,
137  AC,
138  AV
139 };
140 
145 {
146  const char *name;
147  double nvalue;
148 };
149 
153 struct cvss
154 {
155  double conf_impact;
156  double integ_impact;
157  double avail_impact;
158  double access_vector;
160  double authentication;
161 };
162 
163 static const struct impact_item impact_map[][3] = {
164  [A] =
165  {
166  {"N", A_NONE},
167  {"P", A_PARTIAL},
168  {"C", A_COMPLETE},
169  },
170  [I] =
171  {
172  {"N", I_NONE},
173  {"P", I_PARTIAL},
174  {"C", I_COMPLETE},
175  },
176  [C] =
177  {
178  {"N", C_NONE},
179  {"P", C_PARTIAL},
180  {"C", C_COMPLETE},
181  },
182  [Au] =
183  {
184  {"N", Au_NONE},
185  {"M", Au_MULTIPLE_INSTANCES},
186  {"S", Au_SINGLE_INSTANCE},
187  },
188  [AV] =
189  {
190  {"N", AV_NETWORK},
191  {"A", AV_ADJACENT_NETWORK},
192  {"L", AV_LOCAL},
193  },
194  [AC] =
195  {
196  {"L", AC_LOW},
197  {"M", AC_MEDIUM},
198  {"H", AC_HIGH},
199  },
200 };
201 
210 static int
211 toenum (const char *str, enum base_metrics *res)
212 {
213  int rc = 0; /* let's be optimistic */
214 
215  if (g_strcmp0 (str, "A") == 0)
216  *res = A;
217  else if (g_strcmp0 (str, "I") == 0)
218  *res = I;
219  else if (g_strcmp0 (str, "C") == 0)
220  *res = C;
221  else if (g_strcmp0 (str, "Au") == 0)
222  *res = Au;
223  else if (g_strcmp0 (str, "AU") == 0)
224  *res = Au;
225  else if (g_strcmp0 (str, "AV") == 0)
226  *res = AV;
227  else if (g_strcmp0 (str, "AC") == 0)
228  *res = AC;
229  else
230  rc = -1;
231 
232  return rc;
233 }
234 
243 static double
245 {
246  return 10.41
247  * (1
248  - (1 - cvss->conf_impact) * (1 - cvss->integ_impact)
249  * (1 - cvss->avail_impact));
250 }
251 
260 static double
262 {
263  return 20 * cvss->access_vector * cvss->access_complexity
264  * cvss->authentication;
265 }
266 
276 static inline int
277 set_impact_from_str (const char *value, enum base_metrics metric,
278  struct cvss *cvss)
279 {
280  int i;
281 
282  for (i = 0; i < 3; i++)
283  {
284  const struct impact_item *impact;
285 
286  impact = &impact_map[metric][i];
287 
288  if (g_strcmp0 (impact->name, value) == 0)
289  {
290  switch (metric)
291  {
292  case A:
293  cvss->avail_impact = impact->nvalue;
294  break;
295 
296  case I:
297  cvss->integ_impact = impact->nvalue;
298  break;
299 
300  case C:
301  cvss->conf_impact = impact->nvalue;
302  break;
303 
304  case Au:
305  cvss->authentication = impact->nvalue;
306  break;
307 
308  case AV:
309  cvss->access_vector = impact->nvalue;
310  break;
311 
312  case AC:
313  cvss->access_complexity = impact->nvalue;
314  break;
315 
316  default:
317  return -1;
318  }
319  return 0;
320  }
321  }
322  return -1;
323 }
324 
333 static double
335 {
336  double impact = 1.176;
337  double impact_sub;
338  double exploitability_sub;
339 
340  impact_sub = get_impact_subscore (cvss);
341  exploitability_sub = get_exploitability_subscore (cvss);
342 
343  if (impact_sub < 0.1)
344  impact = 0.0;
345 
346  return (((0.6 * impact_sub) + (0.4 * exploitability_sub) - 1.5) * impact)
347  + 0.0;
348 }
349 
357 double
358 get_cvss_score_from_base_metrics (const char *cvss_str)
359 {
360  struct cvss cvss;
361  char *token, *base_str, *base_metrics;
362 
363  if (cvss_str == NULL)
364  return -1.0;
365 
366  if (g_str_has_prefix (cvss_str, "CVSS:3.1/")
367  || g_str_has_prefix (cvss_str, "CVSS:3.0/"))
368  return get_cvss_score_from_base_metrics_v3 (cvss_str
369  + strlen ("CVSS:3.X/"));
370 
371  memset (&cvss, 0x00, sizeof (struct cvss));
372 
373  base_str = base_metrics = g_strdup_printf ("%s/", cvss_str);
374 
375  while ((token = strchr (base_metrics, '/')) != NULL)
376  {
377  char *token2 = strtok (base_metrics, ":");
378  char *metric_name = token2;
379  char *metric_value;
380  enum base_metrics mval;
381  int rc;
382 
383  *token++ = '\0';
384 
385  if (metric_name == NULL)
386  goto ret_err;
387 
388  metric_value = strtok (NULL, ":");
389 
390  if (metric_value == NULL)
391  goto ret_err;
392 
393  rc = toenum (metric_name, &mval);
394  if (rc)
395  goto ret_err;
396 
397  if (set_impact_from_str (metric_value, mval, &cvss))
398  goto ret_err;
399 
400  base_metrics = token;
401  }
402 
403  g_free (base_str);
404  return __get_cvss_score (&cvss);
405 
406 ret_err:
407  g_free (base_str);
408  return (double) -1;
409 }
410 
411 /* CVSS v3. */
412 
420 static double
421 roundup (double cvss)
422 {
423  int trim;
424 
425  /* "Roundup returns the smallest number, specified to 1 decimal place,
426  * that is equal to or higher than its input. For example, Roundup (4.02)
427  * returns 4.1; and Roundup (4.00) returns 4.0." */
428 
429  /* 3.020000000 => 3.1 */
430  /* 3.000000001 => 3.0 */
431  /* 5.299996 => 5.3 */
432  /* 5.500320 => 5.6 */
433 
434  trim = round (cvss * 100000);
435  if ((trim % 10000) == 0)
436  return ((double) trim) / 100000;
437  return (floor (trim / 10000) + 1) / 10.0;
438 }
439 
447 static double
448 v3_impact (const char *value)
449 {
450  if (strcasecmp (value, "N") == 0)
451  return 0.0;
452  if (strcasecmp (value, "L") == 0)
453  return 0.22;
454  if (strcasecmp (value, "H") == 0)
455  return 0.56;
456  return -1.0;
457 }
458 
466 static double
468 {
469  gchar **split, **point;
470  int scope_changed;
471  double impact_conf, impact_integ, impact_avail;
472  double vector, complexity, privilege, user;
473  double isc_base, impact, exploitability, base;
474 
475  /* https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
476  * https://www.first.org/cvss/v3.1/specification-document
477  * https://www.first.org/cvss/v3.0/specification-document */
478 
479  scope_changed = -1;
480  impact_conf = -1.0;
481  impact_integ = -1.0;
482  impact_avail = -1.0;
483  vector = -1.0;
484  complexity = -1.0;
485  privilege = -1.0;
486  user = -1.0;
487 
488  /* AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N */
489 
490  split = g_strsplit (cvss_str, "/", 0);
491  point = split;
492  while (*point)
493  {
494  /* Scope. */
495  if (strncasecmp ("S:", *point, 2) == 0)
496  {
497  if (strcasecmp (*point + 2, "U") == 0)
498  scope_changed = 0;
499  else if (strcasecmp (*point + 2, "C") == 0)
500  scope_changed = 1;
501  }
502 
503  /* Confidentiality. */
504  if (strncasecmp ("C:", *point, 2) == 0)
505  impact_conf = v3_impact (*point + 2);
506 
507  /* Integrity. */
508  if (strncasecmp ("I:", *point, 2) == 0)
509  impact_integ = v3_impact (*point + 2);
510 
511  /* Availability. */
512  if (strncasecmp ("A:", *point, 2) == 0)
513  impact_avail = v3_impact (*point + 2);
514 
515  /* Attack Vector. */
516  if (strncasecmp ("AV:", *point, 3) == 0)
517  {
518  if (strcasecmp (*point + 3, "N") == 0)
519  vector = 0.85;
520  else if (strcasecmp (*point + 3, "A") == 0)
521  vector = 0.62;
522  else if (strcasecmp (*point + 3, "L") == 0)
523  vector = 0.55;
524  else if (strcasecmp (*point + 3, "P") == 0)
525  vector = 0.2;
526  }
527 
528  /* Attack Complexity. */
529  if (strncasecmp ("AC:", *point, 3) == 0)
530  {
531  if (strcasecmp (*point + 3, "L") == 0)
532  complexity = 0.77;
533  else if (strcasecmp (*point + 3, "H") == 0)
534  complexity = 0.44;
535  }
536 
537  /* Privileges Required. */
538  if (strncasecmp ("PR:", *point, 3) == 0)
539  {
540  if (strcasecmp (*point + 3, "N") == 0)
541  privilege = 0.85;
542  else if (strcasecmp (*point + 3, "L") == 0)
543  privilege = 0.62;
544  else if (strcasecmp (*point + 3, "H") == 0)
545  privilege = 0.27;
546  else
547  privilege = -1.0;
548  }
549 
550  /* User Interaction. */
551  if (strncasecmp ("UI:", *point, 3) == 0)
552  {
553  if (strcasecmp (*point + 3, "N") == 0)
554  user = 0.85;
555  else if (strcasecmp (*point + 3, "R") == 0)
556  user = 0.62;
557  }
558 
559  point++;
560  }
561 
562  g_strfreev (split);
563 
564  /* All of the base metrics are required. */
565 
566  if (scope_changed == -1 || impact_conf == -1.0 || impact_integ == -1.0
567  || impact_avail == -1.0 || vector == -1.0 || complexity == -1.0
568  || privilege == -1.0 || user == -1.0)
569  return -1.0;
570 
571  /* Privileges Required has a special case for S:C. */
572 
573  if (scope_changed && privilege == 0.62)
574  privilege = 0.68;
575  else if (scope_changed && privilege == 0.27)
576  privilege = 0.5;
577 
578  /* Impact. */
579 
580  isc_base = 1 - ((1 - impact_conf) * (1 - impact_integ) * (1 - impact_avail));
581 
582  if (scope_changed)
583  impact = 7.52 * (isc_base - 0.029) - 3.25 * pow ((isc_base - 0.02), 15);
584  else
585  impact = 6.42 * isc_base;
586 
587  if (impact <= 0)
588  return 0.0;
589 
590  /* Exploitability. */
591 
592  exploitability = 8.22 * vector * complexity * privilege * user;
593 
594  /* Final. */
595 
596  if (scope_changed)
597  base = 1.08 * (impact + exploitability);
598  else
599  base = impact + exploitability;
600 
601  if (base > 10.0)
602  return 10.0;
603 
604  return roundup (base);
605 }
AV_ADJACENT_NETWORK
#define AV_ADJACENT_NETWORK
Definition: cvss.c:89
AC_MEDIUM
#define AC_MEDIUM
Definition: cvss.c:96
A_PARTIAL
#define A_PARTIAL
Definition: cvss.c:124
__get_cvss_score
static double __get_cvss_score(struct cvss *cvss)
Final CVSS score computation helper.
Definition: cvss.c:334
impact_map
static const struct impact_item impact_map[][3]
Definition: cvss.c:163
I_COMPLETE
#define I_COMPLETE
Definition: cvss.c:118
toenum
static int toenum(const char *str, enum base_metrics *res)
Determine base metric enumeration from a string.
Definition: cvss.c:211
cvss::avail_impact
double avail_impact
Definition: cvss.c:157
C
@ C
Definition: cvss.c:135
get_exploitability_subscore
static double get_exploitability_subscore(const struct cvss *cvss)
Calculate Exploitability Sub Score.
Definition: cvss.c:261
A
@ A
Definition: cvss.c:133
A_NONE
#define A_NONE
AvailabilityImpact (A) Constants.
Definition: cvss.c:123
impact_item::nvalue
double nvalue
Definition: cvss.c:147
get_cvss_score_from_base_metrics_v3
static double get_cvss_score_from_base_metrics_v3(const char *)
Calculate CVSS Score.
Definition: cvss.c:467
I
@ I
Definition: cvss.c:134
Au_SINGLE_INSTANCE
#define Au_SINGLE_INSTANCE
Definition: cvss.c:103
cvss
Describe a CVSS metrics.
Definition: cvss.c:154
Au_NONE
#define Au_NONE
Definition: cvss.c:104
I_NONE
#define I_NONE
IntegrityImpact (I) Constants.
Definition: cvss.c:116
AC
@ AC
Definition: cvss.c:137
get_impact_subscore
static double get_impact_subscore(const struct cvss *cvss)
Calculate Impact Sub Score.
Definition: cvss.c:244
roundup
static double roundup(double cvss)
Round final score as in spec.
Definition: cvss.c:421
AC_HIGH
#define AC_HIGH
Definition: cvss.c:97
impact_item::name
const char * name
Definition: cvss.c:146
C_PARTIAL
#define C_PARTIAL
Definition: cvss.c:110
C_COMPLETE
#define C_COMPLETE
Definition: cvss.c:111
impact_item
Describe a CVSS impact element.
Definition: cvss.c:145
I_PARTIAL
#define I_PARTIAL
Definition: cvss.c:117
cvss::authentication
double authentication
Definition: cvss.c:160
A_COMPLETE
#define A_COMPLETE
Definition: cvss.c:125
C_NONE
#define C_NONE
ConfidentialityImpact (C) Constants.
Definition: cvss.c:109
strings.h
String utilities.
Au
@ Au
Definition: cvss.c:136
AC_LOW
#define AC_LOW
AccessComplexity (AC) Constants.
Definition: cvss.c:95
get_cvss_score_from_base_metrics
double get_cvss_score_from_base_metrics(const char *cvss_str)
Calculate CVSS Score.
Definition: cvss.c:358
AV
@ AV
Definition: cvss.c:138
base_metrics
base_metrics
Base metrics.
Definition: cvss.c:132
v3_impact
static double v3_impact(const char *value)
Get impact.
Definition: cvss.c:448
cvss.h
Protos for CVSS utility functions.
cvss::access_complexity
double access_complexity
Definition: cvss.c:159
set_impact_from_str
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:277
AV_LOCAL
#define AV_LOCAL
Definition: cvss.c:90
cvss::conf_impact
double conf_impact
Definition: cvss.c:155
cvss::access_vector
double access_vector
Definition: cvss.c:158
AV_NETWORK
#define AV_NETWORK
AccessVector (AV) Constants.
Definition: cvss.c:88
Au_MULTIPLE_INSTANCES
#define Au_MULTIPLE_INSTANCES
Authentication (Au) Constants.
Definition: cvss.c:102
cvss::integ_impact
double integ_impact
Definition: cvss.c:156