Coverage Report

Created: 2026-04-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-security-attrs.c
Line
Count
Source
1
/*
2
 * Copyright 2020 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuSecurityAttrs"
8
9
#include "config.h"
10
11
#include <fwupd.h>
12
#include <glib/gi18n.h>
13
14
#include "fwupd-security-attr-private.h"
15
16
#include "fu-security-attr.h"
17
#include "fu-security-attrs-private.h"
18
#include "fu-security-attrs.h"
19
20
/**
21
 * FuSecurityAttrs:
22
 *
23
 * A set of Host Security ID attributes that represents the system state.
24
 */
25
26
struct _FuSecurityAttrs {
27
  GObject parent_instance;
28
  GPtrArray *attrs;
29
};
30
31
/* probably sane to *not* make this part of the ABI */
32
0
#define FWUPD_SECURITY_ATTR_ID_DOC_URL "https://fwupd.github.io/libfwupdplugin/hsi.html"
33
34
static void
35
fu_security_attrs_codec_iface_init(FwupdCodecInterface *iface);
36
37
0
G_DEFINE_TYPE_WITH_CODE(FuSecurityAttrs,
38
0
      fu_security_attrs,
39
0
      G_TYPE_OBJECT,
40
0
      G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_security_attrs_codec_iface_init))
41
0
42
0
static void
43
0
fu_security_attrs_finalize(GObject *obj)
44
0
{
45
0
  FuSecurityAttrs *self = FU_SECURITY_ATTRS(obj);
46
0
  g_ptr_array_unref(self->attrs);
47
0
  G_OBJECT_CLASS(fu_security_attrs_parent_class)->finalize(obj);
48
0
}
49
50
static void
51
fu_security_attrs_class_init(FuSecurityAttrsClass *klass)
52
0
{
53
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
54
0
  object_class->finalize = fu_security_attrs_finalize;
55
0
}
56
57
static void
58
fu_security_attrs_init(FuSecurityAttrs *self)
59
0
{
60
0
  self->attrs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
61
0
}
62
63
/**
64
 * fu_security_attrs_append_internal:
65
 * @self: a #FuSecurityAttrs
66
 * @attr: a #FwupdSecurityAttr
67
 *
68
 * Adds a #FwupdSecurityAttr to the array with no sanity checks.
69
 *
70
 * Since: 1.7.1
71
 **/
72
void
73
fu_security_attrs_append_internal(FuSecurityAttrs *self, FwupdSecurityAttr *attr)
74
0
{
75
0
  g_return_if_fail(FU_IS_SECURITY_ATTRS(self));
76
0
  g_return_if_fail(FWUPD_IS_SECURITY_ATTR(attr));
77
0
  g_ptr_array_add(self->attrs, g_object_ref(attr));
78
0
}
79
80
/**
81
 * fu_security_attrs_is_valid:
82
 * @self: a #FuSecurityAttrs
83
 *
84
 * Adds a #FwupdSecurityAttr to the array with no sanity checks.
85
 *
86
 * Returns: %TRUE if the collection is valid
87
 *
88
 * Since: 2.0.7
89
 **/
90
gboolean
91
fu_security_attrs_is_valid(FuSecurityAttrs *self)
92
0
{
93
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), FALSE);
94
0
  return self->attrs->len > 0;
95
0
}
96
97
/**
98
 * fu_security_attrs_append:
99
 * @self: a #FuSecurityAttrs
100
 * @attr: a #FwupdSecurityAttr
101
 *
102
 * Adds a #FwupdSecurityAttr to the array.
103
 *
104
 * Since: 1.5.0
105
 **/
106
void
107
fu_security_attrs_append(FuSecurityAttrs *self, FwupdSecurityAttr *attr)
108
0
{
109
0
  g_return_if_fail(FU_IS_SECURITY_ATTRS(self));
110
0
  g_return_if_fail(FWUPD_IS_SECURITY_ATTR(attr));
111
112
  /* sanity check */
113
0
  if (fwupd_security_attr_get_plugin(attr) == NULL) {
114
0
    g_warning("%s has no plugin set", fwupd_security_attr_get_appstream_id(attr));
115
0
  }
116
117
  /* sanity check, and correctly prefix the URLs with the current mirror */
118
0
  if (fwupd_security_attr_get_url(attr) == NULL) {
119
0
    g_autofree gchar *url = NULL;
120
0
    url = g_strdup_printf("%s#%s",
121
0
              FWUPD_SECURITY_ATTR_ID_DOC_URL,
122
0
              fwupd_security_attr_get_appstream_id(attr));
123
0
    fwupd_security_attr_set_url(attr, url);
124
0
  } else if (g_str_has_prefix(fwupd_security_attr_get_url(attr), "#")) {
125
0
    g_autofree gchar *url = NULL;
126
0
    url = g_strdup_printf("%s%s",
127
0
              FWUPD_SECURITY_ATTR_ID_DOC_URL,
128
0
              fwupd_security_attr_get_url(attr));
129
0
    fwupd_security_attr_set_url(attr, url);
130
0
  }
131
0
  fu_security_attrs_append_internal(self, attr);
132
0
}
133
134
/**
135
 * fu_security_attrs_get_by_appstream_id:
136
 * @self: a #FuSecurityAttrs
137
 * @appstream_id: an ID, e.g. %FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM
138
 * @error: (nullable): optional return location for an error
139
 *
140
 * Gets a specific #FwupdSecurityAttr from the array.
141
 *
142
 * Returns: (transfer full): a #FwupdSecurityAttr or %NULL
143
 *
144
 * Since: 1.9.6
145
 **/
146
FwupdSecurityAttr *
147
fu_security_attrs_get_by_appstream_id(FuSecurityAttrs *self,
148
              const gchar *appstream_id,
149
              GError **error)
150
0
{
151
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL);
152
0
  if (self->attrs->len == 0) {
153
0
    g_set_error_literal(error,
154
0
            FWUPD_ERROR,
155
0
            FWUPD_ERROR_NOT_FOUND,
156
0
            "no attributes are loaded");
157
0
    return NULL;
158
0
  }
159
0
  for (guint i = 0; i < self->attrs->len; i++) {
160
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
161
0
    if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), appstream_id) == 0)
162
0
      return g_object_ref(attr);
163
0
  }
164
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no attr with ID %s", appstream_id);
165
0
  return NULL;
166
0
}
167
168
/**
169
 * fu_security_attrs_to_variant:
170
 * @self: a #FuSecurityAttrs
171
 *
172
 * Serializes the #FwupdSecurityAttr objects.
173
 *
174
 * Returns: a #GVariant or %NULL
175
 *
176
 * Since: 1.5.0
177
 **/
178
GVariant *
179
fu_security_attrs_to_variant(FuSecurityAttrs *self)
180
0
{
181
0
  GVariantBuilder builder;
182
183
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL);
184
185
0
  g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
186
0
  for (guint i = 0; i < self->attrs->len; i++) {
187
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
188
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
189
0
      continue;
190
0
    g_variant_builder_add_value(
191
0
        &builder,
192
0
        fwupd_codec_to_variant(FWUPD_CODEC(attr), FWUPD_CODEC_FLAG_NONE));
193
0
  }
194
0
  return g_variant_new("(aa{sv})", &builder);
195
0
}
196
197
/**
198
 * fu_security_attrs_get_all:
199
 * @self: a #FuSecurityAttrs
200
 * @fwupd_version: (nullable): fwupd version string, e.g. `2.0.7`
201
 *
202
 * Gets all the non-obsoleted attributes in the object.
203
 *
204
 * Returns: (transfer container) (element-type FwupdSecurityAttr): attributes
205
 *
206
 * Since: 1.5.0
207
 **/
208
GPtrArray *
209
fu_security_attrs_get_all(FuSecurityAttrs *self, const gchar *fwupd_version)
210
0
{
211
0
  g_autoptr(GPtrArray) all = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
212
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL);
213
0
  for (guint i = 0; i < self->attrs->len; i++) {
214
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
215
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
216
0
      continue;
217
0
    if (!fu_security_attr_check_fwupd_version(attr, fwupd_version))
218
0
      continue;
219
0
    g_ptr_array_add(all, g_object_ref(attr));
220
0
  }
221
0
  return g_steal_pointer(&all);
222
0
}
223
224
/**
225
 * fu_security_attrs_get_all_mutable: (skip):
226
 **/
227
GPtrArray *
228
fu_security_attrs_get_all_mutable(FuSecurityAttrs *self)
229
0
{
230
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL);
231
0
  return g_ptr_array_ref(self->attrs);
232
0
}
233
234
/**
235
 * fu_security_attrs_remove_all:
236
 * @self: a #FuSecurityAttrs
237
 *
238
 * Removes all the attributes in the object.
239
 *
240
 * Since: 1.5.0
241
 **/
242
void
243
fu_security_attrs_remove_all(FuSecurityAttrs *self)
244
0
{
245
0
  g_return_if_fail(FU_IS_SECURITY_ATTRS(self));
246
0
  return g_ptr_array_set_size(self->attrs, 0);
247
0
}
248
249
/**
250
 * fu_security_attrs_calculate_hsi:
251
 * @self: a #FuSecurityAttrs
252
 * @fwupd_version: fwupd version, e.g. `2.0.7`
253
 * @flags: HSI attribute flags
254
 *
255
 * Calculates the HSI string from the appended attributes.
256
 *
257
 * Returns: (transfer full): a string or %NULL
258
 *
259
 * Since: 2.0.7
260
 **/
261
gchar *
262
fu_security_attrs_calculate_hsi(FuSecurityAttrs *self,
263
        const gchar *fwupd_version,
264
        FuSecurityAttrsFlags flags)
265
0
{
266
0
  guint hsi_number = 0;
267
0
  FwupdSecurityAttrFlags attr_flags = FWUPD_SECURITY_ATTR_FLAG_NONE;
268
0
  g_autoptr(GString) str = g_string_new("HSI:");
269
0
  const FwupdSecurityAttrFlags hpi_suffixes[] = {
270
0
      FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE,
271
0
      FWUPD_SECURITY_ATTR_FLAG_NONE,
272
0
  };
273
274
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(self), NULL);
275
276
  /* find the highest HSI number where there are no failures and at least
277
   * one success */
278
0
  for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) {
279
0
    gboolean success_cnt = 0;
280
0
    gboolean failure_cnt = 0;
281
0
    for (guint i = 0; i < self->attrs->len; i++) {
282
0
      FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
283
0
      if (fwupd_security_attr_get_level(attr) != j)
284
0
        continue;
285
0
      if (!fu_security_attr_check_fwupd_version(attr, fwupd_version))
286
0
        continue;
287
0
      if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
288
0
        success_cnt++;
289
0
      else if (!fwupd_security_attr_has_flag(attr,
290
0
                     FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
291
0
        failure_cnt++;
292
0
    }
293
294
    /* abort */
295
0
    if (failure_cnt > 0)
296
0
      break;
297
298
    /* we matched at least one thing on this level */
299
0
    if (success_cnt > 0)
300
0
      hsi_number = j;
301
0
  }
302
303
  /* get a logical OR of the runtime flags */
304
0
  for (guint i = 0; i < self->attrs->len; i++) {
305
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
306
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
307
0
      continue;
308
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) &&
309
0
        fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
310
0
      continue;
311
0
    if (!fu_security_attr_check_fwupd_version(attr, fwupd_version))
312
0
      continue;
313
314
0
    attr_flags |= fwupd_security_attr_get_flags(attr);
315
0
  }
316
317
0
  g_string_append_printf(str, "%u", hsi_number);
318
0
  if (attr_flags & FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) {
319
0
    for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) {
320
0
      if (attr_flags & hpi_suffixes[j])
321
0
        g_string_append(
322
0
            str,
323
0
            fwupd_security_attr_flag_to_suffix(hpi_suffixes[j]));
324
0
    }
325
0
  }
326
327
0
  if (flags & FU_SECURITY_ATTRS_FLAG_ADD_VERSION) {
328
0
    g_string_append_printf(str,
329
0
               " (v%d.%d.%d)",
330
0
               FWUPD_MAJOR_VERSION,
331
0
               FWUPD_MINOR_VERSION,
332
0
               FWUPD_MICRO_VERSION);
333
0
  }
334
335
0
  return g_string_free(g_steal_pointer(&str), FALSE);
336
0
}
337
338
static gchar *
339
fu_security_attrs_get_sort_key(FwupdSecurityAttr *attr)
340
0
{
341
0
  GString *str = g_string_new(NULL);
342
343
  /* level */
344
0
  g_string_append_printf(str, "%u", fwupd_security_attr_get_level(attr));
345
346
  /* success -> fail -> obsoletes */
347
0
  if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
348
0
    g_string_append(str, "0");
349
0
  } else if (!fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS) &&
350
0
       !fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) {
351
0
    g_string_append(str, "1");
352
0
  } else {
353
0
    g_string_append(str, "9");
354
0
  }
355
356
  /* prefer name, but fallback to appstream-id for tests */
357
0
  if (fwupd_security_attr_get_name(attr) != NULL) {
358
0
    g_string_append(str, fwupd_security_attr_get_name(attr));
359
0
  } else {
360
0
    g_string_append(str, fwupd_security_attr_get_appstream_id(attr));
361
0
  }
362
0
  return g_string_free(str, FALSE);
363
0
}
364
365
static gint
366
fu_security_attrs_sort_cb(gconstpointer item1, gconstpointer item2)
367
0
{
368
0
  FwupdSecurityAttr *attr1 = *((FwupdSecurityAttr **)item1);
369
0
  FwupdSecurityAttr *attr2 = *((FwupdSecurityAttr **)item2);
370
0
  g_autofree gchar *sort1 = fu_security_attrs_get_sort_key(attr1);
371
0
  g_autofree gchar *sort2 = fu_security_attrs_get_sort_key(attr2);
372
0
  return g_strcmp0(sort1, sort2);
373
0
}
374
375
const struct {
376
  const gchar *appstream_id;
377
  FwupdSecurityAttrLevel level;
378
} appstream_id_level_map[] = {
379
    {FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION},
380
    {FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
381
    {FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
382
    {FWUPD_SECURITY_ATTR_ID_AMD_PLATFORM_SECURE_BOOT, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
383
    {FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION},
384
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
385
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
386
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
387
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
388
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
389
    {FWUPD_SECURITY_ATTR_ID_CET_ACTIVE, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
390
    {FWUPD_SECURITY_ATTR_ID_CET_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
391
    {FWUPD_SECURITY_ATTR_ID_INTEL_GDS, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
392
    {FWUPD_SECURITY_ATTR_ID_SMAP, FWUPD_SECURITY_ATTR_LEVEL_SYSTEM_PROTECTION},
393
    {FWUPD_SECURITY_ATTR_ID_IOMMU, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
394
    {FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
395
    {FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
396
    {FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
397
    {FWUPD_SECURITY_ATTR_ID_MEI_VERSION, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
398
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
399
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
400
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
401
    {FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
402
    {FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
403
    {FWUPD_SECURITY_ATTR_ID_SPI_BLE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
404
    {FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
405
    {FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
406
    {FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
407
    {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
408
    {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM, FWUPD_SECURITY_ATTR_LEVEL_THEORETICAL},
409
    {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
410
    {FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
411
    {FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
412
    {FWUPD_SECURITY_ATTR_ID_UEFI_PK, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
413
    {FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
414
    {FWUPD_SECURITY_ATTR_ID_UEFI_BOOTSERVICE_VARS, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
415
    {FWUPD_SECURITY_ATTR_ID_BIOS_ROLLBACK_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
416
    {FWUPD_SECURITY_ATTR_ID_BIOS_CAPSULE_UPDATES, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
417
    {FWUPD_SECURITY_ATTR_ID_AMD_SMM_LOCKED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
418
    {FWUPD_SECURITY_ATTR_ID_UEFI_MEMORY_PROTECTION, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
419
    {FWUPD_SECURITY_ATTR_ID_UEFI_DB, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
420
    {FWUPD_SECURITY_ATTR_ID_HP_SURESTART, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
421
    {FWUPD_SECURITY_ATTR_ID_AMD_ENTRY_SIGN, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
422
};
423
424
static void
425
fu_security_attrs_ensure_level(FwupdSecurityAttr *attr)
426
0
{
427
0
  const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr);
428
429
  /* already set */
430
0
  if (fwupd_security_attr_get_level(attr) != FWUPD_SECURITY_ATTR_LEVEL_NONE)
431
0
    return;
432
433
  /* not required */
434
0
  if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE))
435
0
    return;
436
437
  /* map ID to level in one place */
438
0
  for (guint i = 0; i < G_N_ELEMENTS(appstream_id_level_map); i++) {
439
0
    if (g_strcmp0(appstream_id, appstream_id_level_map[i].appstream_id) == 0) {
440
0
      fwupd_security_attr_set_level(attr, appstream_id_level_map[i].level);
441
0
      return;
442
0
    }
443
0
  }
444
445
  /* somebody forgot to add to the level map... */
446
0
  g_warning("cannot map %s to a HSI level, assuming critical", appstream_id);
447
0
  fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL);
448
0
}
449
450
const struct {
451
  const gchar *appstream_id;
452
  const gchar *fwupd_version;
453
} appstream_id_version_map[] = {
454
    {FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION, "1.8.0"},
455
    {FWUPD_SECURITY_ATTR_ID_AMD_SMM_LOCKED, "2.0.2"},
456
    {FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION, "1.8.0"},
457
    {FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION, "1.8.0"},
458
    {FWUPD_SECURITY_ATTR_ID_BIOS_CAPSULE_UPDATES, "1.9.6"},
459
    {FWUPD_SECURITY_ATTR_ID_BIOS_ROLLBACK_PROTECTION, "1.8.8"},
460
    {FWUPD_SECURITY_ATTR_ID_AMD_ENTRY_SIGN, "2.1.2"},
461
    {FWUPD_SECURITY_ATTR_ID_AMD_PLATFORM_SECURE_BOOT, "2.1.1"},
462
    {FWUPD_SECURITY_ATTR_ID_CET_ACTIVE, "2.0.0"},
463
    {FWUPD_SECURITY_ATTR_ID_CET_ENABLED, "2.0.0"},
464
    {FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM, "1.5.0"},
465
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ACM, "1.5.0"},
466
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_ENABLED, "1.5.0"},
467
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_OTP, "1.5.0"},
468
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_POLICY, "1.5.0"},
469
    {FWUPD_SECURITY_ATTR_ID_INTEL_BOOTGUARD_VERIFIED, "1.5.0"},
470
    {FWUPD_SECURITY_ATTR_ID_INTEL_GDS, "1.9.4"},
471
    {FWUPD_SECURITY_ATTR_ID_IOMMU, "1.5.0"},
472
    {FWUPD_SECURITY_ATTR_ID_KERNEL_LOCKDOWN, "1.5.0"},
473
    {FWUPD_SECURITY_ATTR_ID_KERNEL_TAINTED, "1.5.0"},
474
    {FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST, "1.8.7"},
475
    {FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE, "1.5.0"},
476
    {FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP, "1.5.0"},
477
    {FWUPD_SECURITY_ATTR_ID_MEI_VERSION, "1.5.0"},
478
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED, "1.5.0"},
479
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED, "1.8.0"},
480
    {FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED, "1.8.0"},
481
    {FWUPD_SECURITY_ATTR_ID_PREBOOT_DMA_PROTECTION, "1.8.0"},
482
    {FWUPD_SECURITY_ATTR_ID_SMAP, "2.0.0"},
483
    {FWUPD_SECURITY_ATTR_ID_SPI_BIOSWE, "1.5.0"},
484
    {FWUPD_SECURITY_ATTR_ID_SPI_BLE, "1.5.0"},
485
    {FWUPD_SECURITY_ATTR_ID_SPI_DESCRIPTOR, "1.6.0"},
486
    {FWUPD_SECURITY_ATTR_ID_SPI_SMM_BWP, "1.5.0"},
487
    {FWUPD_SECURITY_ATTR_ID_SUPPORTED_CPU, "1.8.0"},
488
    {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_IDLE, "1.5.0"},
489
    {FWUPD_SECURITY_ATTR_ID_SUSPEND_TO_RAM, "1.5.0"},
490
    {FWUPD_SECURITY_ATTR_ID_TPM_EMPTY_PCR, "1.7.2"},
491
    {FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0, "1.5.0"},
492
    {FWUPD_SECURITY_ATTR_ID_TPM_VERSION_20, "1.5.0"},
493
    {FWUPD_SECURITY_ATTR_ID_UEFI_BOOTSERVICE_VARS, "1.9.3"},
494
    {FWUPD_SECURITY_ATTR_ID_UEFI_DB, "2.0.8"},
495
    {FWUPD_SECURITY_ATTR_ID_UEFI_MEMORY_PROTECTION, "2.0.7"},
496
    {FWUPD_SECURITY_ATTR_ID_UEFI_PK, "1.5.0"},
497
    {FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT, "1.5.0"},
498
    {FWUPD_SECURITY_ATTR_ID_HP_SURESTART, "2.1.1"},
499
};
500
501
static void
502
fu_security_attrs_ensure_fwupd_version(FwupdSecurityAttr *attr)
503
0
{
504
0
  const gchar *appstream_id = fwupd_security_attr_get_appstream_id(attr);
505
506
  /* already set */
507
0
  if (fwupd_security_attr_get_fwupd_version(attr) != NULL)
508
0
    return;
509
510
  /* not required */
511
0
  if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE))
512
0
    return;
513
514
  /* map ID to fwupd version in one place */
515
0
  for (guint i = 0; i < G_N_ELEMENTS(appstream_id_version_map); i++) {
516
0
    if (g_strcmp0(appstream_id, appstream_id_version_map[i].appstream_id) == 0) {
517
0
      fwupd_security_attr_set_fwupd_version(
518
0
          attr,
519
0
          appstream_id_version_map[i].fwupd_version);
520
0
      return;
521
0
    }
522
0
  }
523
524
  /* somebody forgot to add to the level map... */
525
0
  g_warning("cannot map %s to a fwupd version", appstream_id);
526
0
}
527
528
/**
529
 * fu_security_attrs_depsolve:
530
 * @self: a #FuSecurityAttrs
531
 *
532
 * Marks any attributes with %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED that have been
533
 * defined as obsoleted by other attributes.
534
 *
535
 * It is only required to call this function once, and should be done when all
536
 * attributes have been added. This will also sort the attrs.
537
 *
538
 * Since: 1.5.0
539
 **/
540
void
541
fu_security_attrs_depsolve(FuSecurityAttrs *self)
542
0
{
543
0
  g_return_if_fail(FU_IS_SECURITY_ATTRS(self));
544
545
  /* assign HSI levels if not already done */
546
0
  for (guint i = 0; i < self->attrs->len; i++) {
547
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
548
0
    fu_security_attrs_ensure_level(attr);
549
0
    fu_security_attrs_ensure_fwupd_version(attr);
550
0
  }
551
552
  /* set flat where required */
553
0
  for (guint i = 0; i < self->attrs->len; i++) {
554
0
    FwupdSecurityAttr *attr = g_ptr_array_index(self->attrs, i);
555
0
    const gchar *attr_id = fwupd_security_attr_get_appstream_id(attr);
556
0
    const gchar *attr_plugin = fwupd_security_attr_get_plugin(attr);
557
0
    GPtrArray *obsoletes = fwupd_security_attr_get_obsoletes(attr);
558
559
0
    for (guint j = 0; j < self->attrs->len; j++) {
560
0
      FwupdSecurityAttr *attr_tmp = g_ptr_array_index(self->attrs, j);
561
0
      const gchar *attr_tmp_id = fwupd_security_attr_get_appstream_id(attr_tmp);
562
0
      const gchar *attr_tmp_plugin = fwupd_security_attr_get_plugin(attr_tmp);
563
564
      /* skip self */
565
0
      if (g_strcmp0(attr_plugin, attr_tmp_plugin) == 0 &&
566
0
          g_strcmp0(attr_id, attr_tmp_id) == 0)
567
0
        continue;
568
569
      /* add duplicate (negative) attributes when obsolete not explicitly set
570
       */
571
0
      if (obsoletes->len == 0) {
572
0
        if (g_strcmp0(attr_id, attr_tmp_id) != 0)
573
0
          continue;
574
0
        if (fwupd_security_attr_has_flag(attr,
575
0
                 FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
576
0
          continue;
577
0
        if (fwupd_security_attr_has_flag(attr_tmp,
578
0
                 FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
579
0
          continue;
580
581
0
        if (fwupd_security_attr_has_obsolete(attr, attr_id))
582
0
          continue;
583
0
        if (fwupd_security_attr_has_obsolete(attr_tmp, attr_id))
584
0
          continue;
585
0
        g_debug("duplicate security attr %s from plugin %s implicitly "
586
0
          "obsoleted by plugin %s",
587
0
          attr_id,
588
0
          attr_plugin,
589
0
          attr_tmp_plugin);
590
0
        fwupd_security_attr_add_obsolete(attr, attr_id);
591
0
      }
592
593
      /* walk all the obsoletes for matches appstream ID or plugin */
594
0
      for (guint k = 0; k < obsoletes->len; k++) {
595
0
        const gchar *obsolete = g_ptr_array_index(obsoletes, k);
596
597
0
        if (g_strcmp0(attr_tmp_id, obsolete) == 0 ||
598
0
            g_strcmp0(attr_tmp_plugin, obsolete) == 0) {
599
0
          g_debug("security attr %s:%s obsoleted by %s:%s",
600
0
            attr_tmp_id,
601
0
            attr_tmp_plugin,
602
0
            attr_id,
603
0
            attr_plugin);
604
0
          fwupd_security_attr_add_flag(
605
0
              attr_tmp,
606
0
              FWUPD_SECURITY_ATTR_FLAG_OBSOLETED);
607
0
        }
608
0
      }
609
0
    }
610
0
  }
611
612
  /* sort */
613
0
  g_ptr_array_sort(self->attrs, fu_security_attrs_sort_cb);
614
0
}
615
616
static void
617
fu_security_attrs_add_json(FwupdCodec *codec, FwupdJsonObject *json_obj, FwupdCodecFlags flags)
618
0
{
619
0
  FuSecurityAttrs *self = FU_SECURITY_ATTRS(codec);
620
0
  g_autoptr(GPtrArray) items = fu_security_attrs_get_all(self, NULL);
621
0
  g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new();
622
623
0
  for (guint i = 0; i < items->len; i++) {
624
0
    FwupdSecurityAttr *attr = g_ptr_array_index(items, i);
625
0
    guint64 created = fwupd_security_attr_get_created(attr);
626
0
    g_autoptr(FwupdJsonObject) json_obj_tmp = fwupd_json_object_new();
627
628
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
629
0
      continue;
630
0
    fwupd_security_attr_set_created(attr, 0);
631
0
    fwupd_codec_to_json(FWUPD_CODEC(attr), json_obj_tmp, FWUPD_CODEC_FLAG_NONE);
632
0
    fwupd_security_attr_set_created(attr, created);
633
0
    fwupd_json_array_add_object(json_arr, json_obj_tmp);
634
0
  }
635
0
  fwupd_json_object_add_array(json_obj, "SecurityAttributes", json_arr);
636
0
}
637
638
static gboolean
639
fu_security_attrs_from_json(FwupdCodec *codec, FwupdJsonObject *json_obj, GError **error)
640
0
{
641
0
  FuSecurityAttrs *self = FU_SECURITY_ATTRS(codec);
642
0
  g_autoptr(FwupdJsonArray) json_arr = NULL;
643
644
  /* this has to exist */
645
0
  json_arr = fwupd_json_object_get_array(json_obj, "SecurityAttributes", error);
646
0
  if (json_arr == NULL)
647
0
    return FALSE;
648
0
  for (guint i = 0; i < fwupd_json_array_get_size(json_arr); i++) {
649
0
    g_autoptr(FwupdJsonObject) json_obj_tmp = NULL;
650
0
    g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new(NULL);
651
652
0
    json_obj_tmp = fwupd_json_array_get_object(json_arr, i, error);
653
0
    if (json_obj_tmp == NULL)
654
0
      return FALSE;
655
0
    if (!fwupd_codec_from_json(FWUPD_CODEC(attr), json_obj_tmp, error))
656
0
      return FALSE;
657
0
    if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
658
0
      continue;
659
0
    fu_security_attrs_append(self, attr);
660
0
  }
661
662
  /* success */
663
0
  return TRUE;
664
0
}
665
666
static void
667
fu_security_attrs_codec_iface_init(FwupdCodecInterface *iface)
668
0
{
669
0
  iface->add_json = fu_security_attrs_add_json;
670
0
  iface->from_json = fu_security_attrs_from_json;
671
0
}
672
673
/**
674
 * fu_security_attrs_compare:
675
 * @attrs1: a #FuSecurityAttrs
676
 * @attrs2: another #FuSecurityAttrs, perhaps newer in some way
677
 *
678
 * Compares the two objects, returning the differences.
679
 *
680
 * If the two sets of attrs are considered the same then an empty array is returned.
681
 * Only the AppStream ID results are compared, extra metadata is ignored.
682
 *
683
 * Returns: (element-type FwupdSecurityAttr) (transfer container): differences
684
 *
685
 * Since: 1.9.2
686
 */
687
GPtrArray *
688
fu_security_attrs_compare(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2)
689
0
{
690
0
  g_autoptr(GHashTable) hash1 = g_hash_table_new(g_str_hash, g_str_equal);
691
0
  g_autoptr(GHashTable) hash2 = g_hash_table_new(g_str_hash, g_str_equal);
692
0
  g_autoptr(GPtrArray) array1 = fu_security_attrs_get_all(attrs1, NULL);
693
0
  g_autoptr(GPtrArray) array2 = fu_security_attrs_get_all(attrs2, NULL);
694
0
  g_autoptr(GPtrArray) results =
695
0
      g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
696
697
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(attrs1), NULL);
698
0
  g_return_val_if_fail(FU_IS_SECURITY_ATTRS(attrs2), NULL);
699
700
  /* create hash tables of appstream-id -> FwupdSecurityAttr */
701
0
  for (guint i = 0; i < array1->len; i++) {
702
0
    FwupdSecurityAttr *attr1 = g_ptr_array_index(array1, i);
703
0
    g_hash_table_insert(hash1,
704
0
            (gpointer)fwupd_security_attr_get_appstream_id(attr1),
705
0
            (gpointer)attr1);
706
0
  }
707
0
  for (guint i = 0; i < array2->len; i++) {
708
0
    FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i);
709
0
    g_hash_table_insert(hash2,
710
0
            (gpointer)fwupd_security_attr_get_appstream_id(attr2),
711
0
            (gpointer)attr2);
712
0
  }
713
714
  /* present in attrs2, not present in attrs1 */
715
0
  for (guint i = 0; i < array2->len; i++) {
716
0
    FwupdSecurityAttr *attr1;
717
0
    FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i);
718
0
    attr1 = g_hash_table_lookup(hash1, fwupd_security_attr_get_appstream_id(attr2));
719
0
    if (attr1 == NULL) {
720
0
      g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr2);
721
0
      g_ptr_array_add(results, g_steal_pointer(&attr));
722
0
      continue;
723
0
    }
724
0
  }
725
726
  /* present in attrs1, not present in attrs2 */
727
0
  for (guint i = 0; i < array1->len; i++) {
728
0
    FwupdSecurityAttr *attr1 = g_ptr_array_index(array1, i);
729
0
    FwupdSecurityAttr *attr2;
730
0
    attr2 = g_hash_table_lookup(hash2, fwupd_security_attr_get_appstream_id(attr1));
731
0
    if (attr2 == NULL) {
732
0
      g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr1);
733
0
      fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_UNKNOWN);
734
0
      fwupd_security_attr_set_result_fallback(
735
0
          attr, /* flip these around */
736
0
          fwupd_security_attr_get_result(attr1));
737
0
      g_ptr_array_add(results, g_steal_pointer(&attr));
738
0
      continue;
739
0
    }
740
0
  }
741
742
  /* find any attributes that differ */
743
0
  for (guint i = 0; i < array2->len; i++) {
744
0
    FwupdSecurityAttr *attr1;
745
0
    FwupdSecurityAttr *attr2 = g_ptr_array_index(array2, i);
746
0
    attr1 = g_hash_table_lookup(hash1, fwupd_security_attr_get_appstream_id(attr2));
747
0
    if (attr1 == NULL)
748
0
      continue;
749
750
    /* result of specific attr differed */
751
0
    if (fwupd_security_attr_get_result(attr1) !=
752
0
        fwupd_security_attr_get_result(attr2)) {
753
0
      g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_copy(attr1);
754
0
      fwupd_security_attr_set_result(attr, fwupd_security_attr_get_result(attr2));
755
0
      fwupd_security_attr_set_result_fallback(
756
0
          attr,
757
0
          fwupd_security_attr_get_result(attr1));
758
0
      fwupd_security_attr_set_flags(attr, fwupd_security_attr_get_flags(attr2));
759
0
      g_ptr_array_add(results, g_steal_pointer(&attr));
760
0
    }
761
0
  }
762
763
  /* success */
764
0
  return g_steal_pointer(&results);
765
0
}
766
767
/**
768
 * fu_security_attrs_equal:
769
 * @attrs1: a #FuSecurityAttrs
770
 * @attrs2: another #FuSecurityAttrs
771
 *
772
 * Tests the objects for equality. Only the AppStream ID results are compared, extra metadata
773
 * is ignored.
774
 *
775
 * Returns: %TRUE if the set of attrs can be considered equal
776
 *
777
 * Since: 1.9.2
778
 */
779
gboolean
780
fu_security_attrs_equal(FuSecurityAttrs *attrs1, FuSecurityAttrs *attrs2)
781
0
{
782
0
  g_autoptr(GPtrArray) compare = fu_security_attrs_compare(attrs1, attrs2);
783
0
  return compare->len == 0;
784
0
}
785
786
/**
787
 * fu_security_attrs_new:
788
 *
789
 * Returns: a security attribute
790
 *
791
 * Since: 1.5.0
792
 **/
793
FuSecurityAttrs *
794
fu_security_attrs_new(void)
795
0
{
796
0
  return g_object_new(FU_TYPE_SECURITY_ATTRS, NULL);
797
0
}