/src/fwupd/libfwupdplugin/fu-efi-signature.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 | | #include "config.h" |
8 | | |
9 | | #include "fu-byte-array.h" |
10 | | #include "fu-bytes.h" |
11 | | #include "fu-common.h" |
12 | | #include "fu-efi-signature-private.h" |
13 | | |
14 | | /** |
15 | | * FuEfiSignature: |
16 | | * |
17 | | * A UEFI Signature as found in an `EFI_SIGNATURE_LIST`. |
18 | | * |
19 | | * See also: [class@FuFirmware] |
20 | | */ |
21 | | |
22 | | typedef struct { |
23 | | FuEfiSignatureKind kind; |
24 | | gchar *owner; |
25 | | } FuEfiSignaturePrivate; |
26 | | |
27 | 0 | G_DEFINE_TYPE_WITH_PRIVATE(FuEfiSignature, fu_efi_signature, FU_TYPE_FIRMWARE) |
28 | 0 | #define GET_PRIVATE(o) (fu_efi_signature_get_instance_private(o)) |
29 | | |
30 | | static void |
31 | | fu_efi_signature_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
32 | 0 | { |
33 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); |
34 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
35 | |
|
36 | 0 | fu_xmlb_builder_insert_kv(bn, "kind", fu_efi_signature_kind_to_string(priv->kind)); |
37 | 0 | fu_xmlb_builder_insert_kv(bn, "owner", priv->owner); |
38 | | |
39 | | /* special case: this is *literally* a hash */ |
40 | 0 | if (priv->kind == FU_EFI_SIGNATURE_KIND_SHA256) { |
41 | 0 | g_autoptr(GBytes) blob = fu_firmware_get_bytes(firmware, NULL); |
42 | 0 | if (blob != NULL) { |
43 | 0 | g_autofree gchar *str = fu_bytes_to_string(blob); |
44 | 0 | fu_xmlb_builder_insert_kv(bn, "checksum", str); |
45 | 0 | } |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | /** |
50 | | * fu_efi_signature_new: (skip): |
51 | | * @kind: A #FuEfiSignatureKind |
52 | | * |
53 | | * Creates a new EFI_SIGNATURE. |
54 | | * |
55 | | * Returns: (transfer full): signature |
56 | | * |
57 | | * Since: 2.0.5 |
58 | | **/ |
59 | | FuEfiSignature * |
60 | | fu_efi_signature_new(FuEfiSignatureKind kind) |
61 | 0 | { |
62 | 0 | g_autoptr(FuEfiSignature) self = g_object_new(FU_TYPE_EFI_SIGNATURE, NULL); |
63 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
64 | 0 | priv->kind = kind; |
65 | 0 | return g_steal_pointer(&self); |
66 | 0 | } |
67 | | |
68 | | /** |
69 | | * fu_efi_signature_get_kind: |
70 | | * @self: A #FuEfiSignature |
71 | | * |
72 | | * Returns the signature kind. |
73 | | * |
74 | | * Returns: #FuEfiSignatureKind, e.g. %FU_EFI_SIGNATURE_KIND_SHA256 |
75 | | * |
76 | | * Since: 1.5.5 |
77 | | **/ |
78 | | FuEfiSignatureKind |
79 | | fu_efi_signature_get_kind(FuEfiSignature *self) |
80 | 0 | { |
81 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
82 | 0 | g_return_val_if_fail(FU_IS_EFI_SIGNATURE(self), FU_EFI_SIGNATURE_KIND_UNKNOWN); |
83 | 0 | return priv->kind; |
84 | 0 | } |
85 | | |
86 | | /* private */ |
87 | | void |
88 | | fu_efi_signature_set_kind(FuEfiSignature *self, FuEfiSignatureKind kind) |
89 | 0 | { |
90 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
91 | 0 | g_return_if_fail(FU_IS_EFI_SIGNATURE(self)); |
92 | 0 | priv->kind = kind; |
93 | 0 | } |
94 | | |
95 | | /** |
96 | | * fu_efi_signature_get_owner: |
97 | | * @self: A #FuEfiSignature |
98 | | * |
99 | | * Returns the GUID of the signature owner. |
100 | | * |
101 | | * Returns: GUID owner, perhaps %FU_EFI_SIGNATURE_GUID_MICROSOFT |
102 | | * |
103 | | * Since: 1.5.5 |
104 | | **/ |
105 | | const gchar * |
106 | | fu_efi_signature_get_owner(FuEfiSignature *self) |
107 | 0 | { |
108 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
109 | 0 | g_return_val_if_fail(FU_IS_EFI_SIGNATURE(self), NULL); |
110 | 0 | return priv->owner; |
111 | 0 | } |
112 | | |
113 | | static gboolean |
114 | | fu_efi_signature_parse(FuFirmware *firmware, |
115 | | GInputStream *stream, |
116 | | FuFirmwareParseFlags flags, |
117 | | GError **error) |
118 | 0 | { |
119 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); |
120 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
121 | 0 | fwupd_guid_t guid = {0}; |
122 | 0 | gsize size = fu_firmware_get_size(firmware); |
123 | 0 | g_autoptr(GBytes) data = NULL; |
124 | | |
125 | | /* sanity check */ |
126 | 0 | if (size <= sizeof(fwupd_guid_t)) { |
127 | 0 | g_set_error(error, |
128 | 0 | FWUPD_ERROR, |
129 | 0 | FWUPD_ERROR_INVALID_DATA, |
130 | 0 | "SignatureSize invalid: 0x%x", |
131 | 0 | (guint)size); |
132 | 0 | return FALSE; |
133 | 0 | } |
134 | | |
135 | | /* GUID */ |
136 | 0 | if (!fu_input_stream_read_safe(stream, |
137 | 0 | (guint8 *)&guid, |
138 | 0 | sizeof(guid), |
139 | 0 | 0x0, /* seek to */ |
140 | 0 | 0x0, /* offset */ |
141 | 0 | sizeof(guid), |
142 | 0 | error)) { |
143 | 0 | g_prefix_error_literal(error, "failed to read signature GUID: "); |
144 | 0 | return FALSE; |
145 | 0 | } |
146 | 0 | priv->owner = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); |
147 | | |
148 | | /* if data size is unspecified, we'll use the stream size */ |
149 | 0 | data = fu_input_stream_read_bytes(stream, |
150 | 0 | sizeof(fwupd_guid_t), |
151 | 0 | size - sizeof(fwupd_guid_t), |
152 | 0 | NULL, |
153 | 0 | error); |
154 | 0 | if (data == NULL) { |
155 | 0 | g_prefix_error_literal(error, "failed to read signature data: "); |
156 | 0 | return FALSE; |
157 | 0 | } |
158 | 0 | fu_firmware_set_bytes(firmware, data); |
159 | | |
160 | | /* success */ |
161 | 0 | return TRUE; |
162 | 0 | } |
163 | | |
164 | | static GByteArray * |
165 | | fu_efi_signature_write(FuFirmware *firmware, GError **error) |
166 | 0 | { |
167 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); |
168 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
169 | 0 | fwupd_guid_t owner = {0}; |
170 | 0 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
171 | 0 | g_autoptr(GBytes) data = NULL; |
172 | | |
173 | | /* optional owner */ |
174 | 0 | if (priv->owner != NULL) { |
175 | 0 | if (!fwupd_guid_from_string(priv->owner, |
176 | 0 | &owner, |
177 | 0 | FWUPD_GUID_FLAG_MIXED_ENDIAN, |
178 | 0 | error)) |
179 | 0 | return NULL; |
180 | 0 | } |
181 | 0 | g_byte_array_append(buf, owner, sizeof(owner)); |
182 | | |
183 | | /* data */ |
184 | 0 | data = fu_firmware_get_bytes_with_patches(firmware, error); |
185 | 0 | if (data == NULL) |
186 | 0 | return NULL; |
187 | 0 | fu_byte_array_append_bytes(buf, data); |
188 | | |
189 | | /* success */ |
190 | 0 | return g_steal_pointer(&buf); |
191 | 0 | } |
192 | | |
193 | | static gboolean |
194 | | fu_efi_signature_build(FuFirmware *firmware, XbNode *n, GError **error) |
195 | 0 | { |
196 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); |
197 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
198 | 0 | const gchar *tmp; |
199 | | |
200 | | /* optional properties */ |
201 | 0 | tmp = xb_node_query_text(n, "kind", NULL); |
202 | 0 | if (tmp != NULL) { |
203 | 0 | priv->kind = fu_efi_signature_kind_from_string(tmp); |
204 | 0 | if (priv->kind == FU_EFI_SIGNATURE_KIND_UNKNOWN) { |
205 | 0 | g_set_error(error, |
206 | 0 | FWUPD_ERROR, |
207 | 0 | FWUPD_ERROR_INVALID_DATA, |
208 | 0 | "invalid kind: %s", |
209 | 0 | tmp); |
210 | 0 | return FALSE; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | tmp = xb_node_query_text(n, "owner", NULL); |
214 | 0 | if (tmp != NULL) { |
215 | 0 | if (!fwupd_guid_from_string(tmp, NULL, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) { |
216 | 0 | g_prefix_error(error, "failed to parse owner %s, expected GUID: ", tmp); |
217 | 0 | return FALSE; |
218 | 0 | } |
219 | 0 | g_free(priv->owner); |
220 | 0 | priv->owner = g_strdup(tmp); |
221 | 0 | } |
222 | 0 | tmp = xb_node_query_text(n, "checksum", NULL); |
223 | 0 | if (tmp != NULL) { |
224 | 0 | g_autoptr(GBytes) data = NULL; |
225 | 0 | data = fu_bytes_from_string(tmp, error); |
226 | 0 | if (data == NULL) |
227 | 0 | return FALSE; |
228 | 0 | fu_firmware_set_bytes(firmware, data); |
229 | 0 | } |
230 | | |
231 | | /* success */ |
232 | 0 | return TRUE; |
233 | 0 | } |
234 | | |
235 | | static gchar * |
236 | | fu_efi_signature_get_checksum(FuFirmware *firmware, GChecksumType csum_kind, GError **error) |
237 | 0 | { |
238 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); |
239 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
240 | 0 | g_autoptr(GBytes) data = fu_firmware_get_bytes_with_patches(firmware, error); |
241 | 0 | if (data == NULL) |
242 | 0 | return NULL; |
243 | | |
244 | | /* special case: this is *literally* a hash */ |
245 | 0 | if (priv->kind == FU_EFI_SIGNATURE_KIND_SHA256 && csum_kind == G_CHECKSUM_SHA256) |
246 | 0 | return fu_bytes_to_string(data); |
247 | | |
248 | | /* fallback */ |
249 | 0 | return g_compute_checksum_for_bytes(csum_kind, data); |
250 | 0 | } |
251 | | |
252 | | static void |
253 | | fu_efi_signature_finalize(GObject *obj) |
254 | 0 | { |
255 | 0 | FuEfiSignature *self = FU_EFI_SIGNATURE(obj); |
256 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
257 | 0 | g_free(priv->owner); |
258 | 0 | G_OBJECT_CLASS(fu_efi_signature_parent_class)->finalize(obj); |
259 | 0 | } |
260 | | |
261 | | static void |
262 | | fu_efi_signature_class_init(FuEfiSignatureClass *klass) |
263 | 0 | { |
264 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
265 | 0 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
266 | 0 | object_class->finalize = fu_efi_signature_finalize; |
267 | 0 | firmware_class->export = fu_efi_signature_export; |
268 | 0 | firmware_class->parse = fu_efi_signature_parse; |
269 | 0 | firmware_class->write = fu_efi_signature_write; |
270 | 0 | firmware_class->build = fu_efi_signature_build; |
271 | 0 | firmware_class->get_checksum = fu_efi_signature_get_checksum; |
272 | 0 | } |
273 | | |
274 | | static void |
275 | | fu_efi_signature_init(FuEfiSignature *self) |
276 | 0 | { |
277 | 0 | FuEfiSignaturePrivate *priv = GET_PRIVATE(self); |
278 | 0 | priv->kind = FU_EFI_SIGNATURE_KIND_SHA256; |
279 | 0 | } |