Coverage Report

Created: 2026-01-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-vss-auth-variable.c
Line
Count
Source
1
/*
2
 * Copyright 2025 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
4.68k
#define G_LOG_DOMAIN "FuEfiVssAuthVariable"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-common.h"
13
#include "fu-efi-common.h"
14
#include "fu-efi-signature-list.h"
15
#include "fu-efi-struct.h"
16
#include "fu-efi-vss-auth-variable.h"
17
#include "fu-efivars.h"
18
#include "fu-partial-input-stream.h"
19
#include "fu-string.h"
20
21
/**
22
 * FuEfiVssAuthVariable:
23
 *
24
 * A NVRAM authenticated variable
25
 *
26
 * See also: [class@FuFirmware]
27
 */
28
29
struct _FuEfiVssAuthVariable {
30
  FuFirmware parent_instance;
31
  gchar *vendor_guid;
32
  FuEfiVariableAttrs attributes;
33
  FuEfiVariableState state;
34
  FuStructEfiTime *timestamp; /* nullable */
35
};
36
37
89.3k
G_DEFINE_TYPE(FuEfiVssAuthVariable, fu_efi_vss_auth_variable, FU_TYPE_FIRMWARE)
38
89.3k
39
89.3k
/**
40
89.3k
 * fu_efi_vss_auth_variable_get_state:
41
89.3k
 * @self: #FuEfiVssAuthVariable
42
89.3k
 *
43
89.3k
 * Gets the VSS variable state.
44
89.3k
 *
45
89.3k
 * Returns: a #FuEfiVariableState, e.g. %FU_EFI_VARIABLE_STATE_VARIABLE_ADDED
46
89.3k
 *
47
89.3k
 * Since: 2.0.17
48
89.3k
 **/
49
89.3k
FuEfiVariableState
50
89.3k
fu_efi_vss_auth_variable_get_state(FuEfiVssAuthVariable *self)
51
89.3k
{
52
4.37k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FU_EFI_VARIABLE_STATE_UNSET);
53
4.37k
  return self->state;
54
4.37k
}
55
56
static void
57
fu_efi_vss_auth_variable_export(FuFirmware *firmware,
58
        FuFirmwareExportFlags flags,
59
        XbBuilderNode *bn)
60
0
{
61
0
  FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware);
62
0
  fu_xmlb_builder_insert_kv(bn, "vendor_guid", self->vendor_guid);
63
0
  if (self->state != FU_EFI_VARIABLE_STATE_UNSET) {
64
0
    const gchar *str = fu_efi_variable_state_to_string(self->state);
65
0
    fu_xmlb_builder_insert_kv(bn, "state", str);
66
0
  }
67
0
  if (self->attributes != FU_EFI_VARIABLE_ATTR_NONE) {
68
0
    g_autofree gchar *str = fu_efi_variable_attrs_to_string(self->attributes);
69
0
    fu_xmlb_builder_insert_kv(bn, "attributes", str);
70
0
  }
71
0
  if (self->timestamp != NULL) {
72
0
    g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "timestamp", NULL);
73
0
    fu_efi_timestamp_export(self->timestamp, bc);
74
0
  }
75
0
}
76
77
static GType
78
fu_efi_vss_auth_variable_lookup_image_gtype(FuEfiVssAuthVariable *self)
79
4.68k
{
80
4.68k
  struct {
81
4.68k
    const gchar *guid;
82
4.68k
    const gchar *name;
83
4.68k
    GType gtype;
84
4.68k
  } gtypes[] = {
85
4.68k
      {FU_EFIVARS_GUID_EFI_GLOBAL, "PK", FU_TYPE_EFI_SIGNATURE_LIST},
86
4.68k
      {FU_EFIVARS_GUID_EFI_GLOBAL, "KEK", FU_TYPE_EFI_SIGNATURE_LIST},
87
4.68k
      {FU_EFIVARS_GUID_SECURITY_DATABASE, "db", FU_TYPE_EFI_SIGNATURE_LIST},
88
4.68k
      {FU_EFIVARS_GUID_SECURITY_DATABASE, "dbx", FU_TYPE_EFI_SIGNATURE_LIST},
89
4.68k
  };
90
23.4k
  for (guint i = 0; i < G_N_ELEMENTS(gtypes); i++) {
91
18.7k
    if (g_strcmp0(gtypes[i].guid, self->vendor_guid) == 0 &&
92
0
        g_strcmp0(gtypes[i].name, fu_firmware_get_id(FU_FIRMWARE(self))) == 0)
93
0
      return gtypes[i].gtype;
94
18.7k
  }
95
4.68k
  return G_TYPE_INVALID;
96
4.68k
}
97
98
static gboolean
99
fu_efi_vss_auth_variable_parse(FuFirmware *firmware,
100
             GInputStream *stream,
101
             FuFirmwareParseFlags flags,
102
             GError **error)
103
5.33k
{
104
5.33k
  FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware);
105
5.33k
  gsize offset = 0x0;
106
5.33k
  GType img_gtype;
107
5.33k
  g_autoptr(FuStructEfiVssAuthVariableHeader) st = NULL;
108
5.33k
  g_autoptr(GByteArray) buf_name = NULL;
109
5.33k
  g_autofree gchar *name = NULL;
110
111
5.33k
  st = fu_struct_efi_vss_auth_variable_header_parse_stream(stream, offset, error);
112
5.33k
  if (st == NULL)
113
109
    return FALSE;
114
5.22k
  if (fu_struct_efi_vss_auth_variable_header_get_start_id(st) == 0xFFFF) {
115
176
    fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE);
116
176
    return TRUE;
117
176
  }
118
5.04k
  if (fu_struct_efi_vss_auth_variable_header_get_start_id(st) !=
119
5.04k
      FU_STRUCT_EFI_VSS_AUTH_VARIABLE_HEADER_DEFAULT_START_ID) {
120
233
    g_set_error(error,
121
233
          FWUPD_ERROR,
122
233
          FWUPD_ERROR_INTERNAL,
123
233
          "invalid VSS variable start ID, expected 0x%x and got 0x%x",
124
233
          (guint)FU_STRUCT_EFI_VSS_AUTH_VARIABLE_HEADER_DEFAULT_START_ID,
125
233
          fu_struct_efi_vss_auth_variable_header_get_start_id(st));
126
233
    return FALSE;
127
233
  }
128
129
  /* attributes we care about */
130
4.81k
  self->vendor_guid =
131
4.81k
      fwupd_guid_to_string(fu_struct_efi_vss_auth_variable_header_get_vendor_guid(st),
132
4.81k
         FWUPD_GUID_FLAG_MIXED_ENDIAN);
133
4.81k
  self->attributes = fu_struct_efi_vss_auth_variable_header_get_attributes(st);
134
4.81k
  self->state = fu_struct_efi_vss_auth_variable_header_get_state(st);
135
4.81k
  self->timestamp = fu_struct_efi_vss_auth_variable_header_get_timestamp(st);
136
137
  /* read name */
138
4.81k
  offset += st->buf->len;
139
4.81k
  buf_name = fu_input_stream_read_byte_array(
140
4.81k
      stream,
141
4.81k
      offset,
142
4.81k
      fu_struct_efi_vss_auth_variable_header_get_name_size(st),
143
4.81k
      NULL,
144
4.81k
      error);
145
4.81k
  if (buf_name == NULL)
146
42
    return FALSE;
147
4.77k
  name = fu_utf16_to_utf8_byte_array(buf_name, G_LITTLE_ENDIAN, error);
148
4.77k
  if (name == NULL)
149
90
    return FALSE;
150
4.68k
  fu_firmware_set_id(firmware, name);
151
4.68k
  g_debug("added %s: %s", self->vendor_guid, name);
152
153
  /* read data */
154
4.68k
  offset += fu_struct_efi_vss_auth_variable_header_get_name_size(st);
155
156
  /* if this is a well known key then parse it as a child type */
157
4.68k
  img_gtype = fu_efi_vss_auth_variable_lookup_image_gtype(self);
158
4.68k
  if (img_gtype != G_TYPE_INVALID) {
159
0
    g_autoptr(FuFirmware) img = g_object_new(img_gtype, NULL);
160
0
    g_autoptr(GInputStream) partial_stream = NULL;
161
0
    partial_stream = fu_partial_input_stream_new(
162
0
        stream,
163
0
        offset,
164
0
        fu_struct_efi_vss_auth_variable_header_get_data_size(st),
165
0
        error);
166
0
    if (partial_stream == NULL)
167
0
      return FALSE;
168
0
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
169
0
      return FALSE;
170
0
    if (!fu_firmware_add_image(firmware, img, error))
171
0
      return FALSE;
172
4.68k
  } else {
173
4.68k
    g_autoptr(GBytes) data = NULL;
174
4.68k
    data = fu_input_stream_read_bytes(
175
4.68k
        stream,
176
4.68k
        offset,
177
4.68k
        fu_struct_efi_vss_auth_variable_header_get_data_size(st),
178
4.68k
        NULL,
179
4.68k
        error);
180
4.68k
    if (data == NULL)
181
304
      return FALSE;
182
4.37k
    fu_firmware_set_bytes(firmware, data);
183
4.37k
  }
184
185
  /* next header */
186
4.37k
  offset += fu_struct_efi_vss_auth_variable_header_get_data_size(st);
187
188
  /* success */
189
4.37k
  fu_firmware_set_size(firmware, offset);
190
4.37k
  return TRUE;
191
4.68k
}
192
193
static GByteArray *
194
fu_efi_vss_auth_variable_write(FuFirmware *firmware, GError **error)
195
525
{
196
525
  FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware);
197
525
  g_autoptr(FuStructEfiVssAuthVariableHeader) st =
198
525
      fu_struct_efi_vss_auth_variable_header_new();
199
525
  g_autoptr(GBytes) name = NULL;
200
525
  g_autoptr(GBytes) blob = NULL;
201
202
  /* attrs */
203
525
  fu_struct_efi_vss_auth_variable_header_set_attributes(st, self->attributes);
204
525
  fu_struct_efi_vss_auth_variable_header_set_state(st, self->state);
205
525
  if (self->timestamp != NULL) {
206
525
    if (!fu_struct_efi_vss_auth_variable_header_set_timestamp(st,
207
525
                    self->timestamp,
208
525
                    error))
209
0
      return NULL;
210
525
  }
211
212
  /* name */
213
525
  if (fu_firmware_get_id(firmware) == NULL) {
214
0
    g_set_error_literal(error,
215
0
            FWUPD_ERROR,
216
0
            FWUPD_ERROR_INVALID_DATA,
217
0
            "firmware ID is required");
218
0
    return NULL;
219
0
  }
220
525
  name = fu_utf8_to_utf16_bytes(fu_firmware_get_id(firmware),
221
525
              G_LITTLE_ENDIAN,
222
525
              FU_UTF_CONVERT_FLAG_APPEND_NUL,
223
525
              error);
224
525
  if (name == NULL)
225
0
    return NULL;
226
525
  fu_struct_efi_vss_auth_variable_header_set_name_size(st, g_bytes_get_size(name));
227
228
  /* data */
229
525
  blob = fu_firmware_get_image_by_id_bytes(firmware, NULL, NULL);
230
525
  if (blob == NULL) {
231
525
    blob = fu_firmware_get_bytes(firmware, error);
232
525
    if (blob == NULL)
233
0
      return NULL;
234
525
  }
235
525
  fu_struct_efi_vss_auth_variable_header_set_data_size(st, g_bytes_get_size(blob));
236
237
  /* guid */
238
525
  if (self->vendor_guid != NULL) {
239
525
    fwupd_guid_t guid = {0};
240
525
    if (!fwupd_guid_from_string(self->vendor_guid,
241
525
              &guid,
242
525
              FWUPD_GUID_FLAG_MIXED_ENDIAN,
243
525
              error))
244
0
      return NULL;
245
525
    fu_struct_efi_vss_auth_variable_header_set_vendor_guid(st, &guid);
246
525
  }
247
248
  /* concat */
249
525
  fu_byte_array_append_bytes(st->buf, name);
250
525
  fu_byte_array_append_bytes(st->buf, blob);
251
252
  /* success */
253
525
  return g_steal_pointer(&st->buf);
254
525
}
255
256
static gboolean
257
fu_efi_vss_auth_variable_build(FuFirmware *firmware, XbNode *n, GError **error)
258
0
{
259
0
  FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware);
260
0
  const gchar *tmp;
261
0
  g_autoptr(XbNode) n_timestamp = NULL;
262
263
  /* simple properties */
264
0
  tmp = xb_node_query_text(n, "vendor_guid", NULL);
265
0
  if (tmp != NULL)
266
0
    self->vendor_guid = g_strdup(tmp);
267
0
  tmp = xb_node_query_text(n, "attributes", NULL);
268
0
  if (tmp != NULL)
269
0
    self->attributes = fu_efi_variable_attrs_from_string(tmp);
270
0
  tmp = xb_node_query_text(n, "state", NULL);
271
0
  if (tmp != NULL)
272
0
    self->state = fu_efi_variable_state_from_string(tmp);
273
274
  /* EFI_TIME */
275
0
  n_timestamp = xb_node_query_first(n, "timestamp", NULL);
276
0
  if (n_timestamp != NULL) {
277
0
    self->timestamp = fu_struct_efi_time_new();
278
0
    fu_efi_timestamp_build(self->timestamp, n_timestamp);
279
0
  }
280
281
  /* success */
282
0
  return TRUE;
283
0
}
284
285
static void
286
fu_efi_vss_auth_variable_init(FuEfiVssAuthVariable *self)
287
5.42k
{
288
5.42k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE);
289
5.42k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_SIGNATURE_LIST);
290
5.42k
}
291
292
static void
293
fu_efi_vss_auth_variable_finalize(GObject *obj)
294
5.42k
{
295
5.42k
  FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(obj);
296
5.42k
  if (self->timestamp != NULL)
297
4.81k
    fu_struct_efi_time_unref(self->timestamp);
298
5.42k
  g_free(self->vendor_guid);
299
5.42k
  G_OBJECT_CLASS(fu_efi_vss_auth_variable_parent_class)->finalize(obj);
300
5.42k
}
301
302
static void
303
fu_efi_vss_auth_variable_class_init(FuEfiVssAuthVariableClass *klass)
304
2
{
305
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
306
2
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
307
2
  object_class->finalize = fu_efi_vss_auth_variable_finalize;
308
2
  firmware_class->parse = fu_efi_vss_auth_variable_parse;
309
2
  firmware_class->export = fu_efi_vss_auth_variable_export;
310
2
  firmware_class->write = fu_efi_vss_auth_variable_write;
311
2
  firmware_class->build = fu_efi_vss_auth_variable_build;
312
2
}
313
314
/**
315
 * fu_efi_vss_auth_variable_new:
316
 *
317
 * Creates an empty VSS variable store.
318
 *
319
 * Returns: a #FuFirmware
320
 *
321
 * Since: 2.0.17
322
 **/
323
FuFirmware *
324
fu_efi_vss_auth_variable_new(void)
325
5.42k
{
326
5.42k
  return g_object_new(FU_TYPE_EFI_VSS_AUTH_VARIABLE, NULL);
327
5.42k
}