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
75#define G_LOG_DOMAIN "libgvm base"
76
77/* Static Headers. */
78
79static 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
140
145{
146 const char *name;
147 double nvalue;
148};
149
162
163static 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},
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
210static int
211toenum (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
243static double
245{
246 return 10.41
247 * (1
248 - (1 - cvss->conf_impact) * (1 - cvss->integ_impact)
249 * (1 - cvss->avail_impact));
250}
251
260static double
266
276static inline int
277set_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
333static 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
357double
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/"))
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
406ret_err:
407 g_free (base_str);
408 return (double) -1;
409}
410
411/* CVSS v3. */
412
420static double
421roundup (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
447static double
448v3_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
466static 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}
#define C_COMPLETE
Definition cvss.c:111
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
#define AV_LOCAL
Definition cvss.c:90
static double get_exploitability_subscore(const struct cvss *cvss)
Calculate Exploitability Sub Score.
Definition cvss.c:261
#define Au_SINGLE_INSTANCE
Definition cvss.c:103
#define A_PARTIAL
Definition cvss.c:124
static int toenum(const char *str, enum base_metrics *res)
Determine base metric enumeration from a string.
Definition cvss.c:211
#define C_NONE
ConfidentialityImpact (C) Constants.
Definition cvss.c:109
#define I_PARTIAL
Definition cvss.c:117
static double v3_impact(const char *value)
Get impact.
Definition cvss.c:448
#define AV_NETWORK
AccessVector (AV) Constants.
Definition cvss.c:88
#define C_PARTIAL
Definition cvss.c:110
#define A_COMPLETE
Definition cvss.c:125
#define Au_MULTIPLE_INSTANCES
Authentication (Au) Constants.
Definition cvss.c:102
static double get_impact_subscore(const struct cvss *cvss)
Calculate Impact Sub Score.
Definition cvss.c:244
#define AC_LOW
AccessComplexity (AC) Constants.
Definition cvss.c:95
#define I_NONE
IntegrityImpact (I) Constants.
Definition cvss.c:116
static const struct impact_item impact_map[][3]
Definition cvss.c:163
base_metrics
Base metrics.
Definition cvss.c:132
@ AC
Definition cvss.c:137
@ A
Definition cvss.c:133
@ C
Definition cvss.c:135
@ Au
Definition cvss.c:136
@ I
Definition cvss.c:134
@ AV
Definition cvss.c:138
static double get_cvss_score_from_base_metrics_v3(const char *)
Calculate CVSS Score.
Definition cvss.c:467
#define A_NONE
AvailabilityImpact (A) Constants.
Definition cvss.c:123
double get_cvss_score_from_base_metrics(const char *cvss_str)
Calculate CVSS Score.
Definition cvss.c:358
static double __get_cvss_score(struct cvss *cvss)
Final CVSS score computation helper.
Definition cvss.c:334
#define I_COMPLETE
Definition cvss.c:118
#define AC_MEDIUM
Definition cvss.c:96
#define AV_ADJACENT_NETWORK
Definition cvss.c:89
static double roundup(double cvss)
Round final score as in spec.
Definition cvss.c:421
#define AC_HIGH
Definition cvss.c:97
#define Au_NONE
Definition cvss.c:104
Protos for CVSS utility functions.
String utilities.
Describe a CVSS metrics.
Definition cvss.c:154
double integ_impact
Definition cvss.c:156
double access_vector
Definition cvss.c:158
double avail_impact
Definition cvss.c:157
double authentication
Definition cvss.c:160
double access_complexity
Definition cvss.c:159
double conf_impact
Definition cvss.c:155
Describe a CVSS impact element.
Definition cvss.c:145
const char * name
Definition cvss.c:146
double nvalue
Definition cvss.c:147