Coverage Report

Created: 2025-11-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-x509-certificate.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
#include "config.h"
8
9
#ifdef HAVE_GNUTLS
10
#include <gnutls/abstract.h>
11
#include <gnutls/crypto.h>
12
#endif
13
14
#include "fu-common.h"
15
#include "fu-input-stream.h"
16
#include "fu-string.h"
17
#include "fu-x509-certificate.h"
18
19
#ifdef HAVE_GNUTLS
20
static void
21
fu_x509_certificate_gnutls_datum_deinit(gnutls_datum_t *d)
22
{
23
  gnutls_free(d->data);
24
  gnutls_free(d);
25
}
26
27
#pragma clang diagnostic push
28
#pragma clang diagnostic ignored "-Wunused-function"
29
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_datum_t, fu_x509_certificate_gnutls_datum_deinit)
30
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL)
31
#pragma clang diagnostic pop
32
#endif
33
34
/**
35
 * FuX509Certificate:
36
 *
37
 * An X.509 certificate.
38
 *
39
 * See also: [class@FuFirmware]
40
 */
41
42
struct _FuX509Certificate {
43
  FuFirmware parent_instance;
44
  gchar *issuer;
45
  gchar *subject;
46
  GDateTime *activation_time;
47
};
48
49
0
G_DEFINE_TYPE(FuX509Certificate, fu_x509_certificate, FU_TYPE_FIRMWARE)
50
0
51
0
static void
52
0
fu_x509_certificate_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
53
0
{
54
0
  FuX509Certificate *self = FU_X509_CERTIFICATE(firmware);
55
0
  fu_xmlb_builder_insert_kv(bn, "issuer", self->issuer);
56
0
  fu_xmlb_builder_insert_kv(bn, "subject", self->subject);
57
0
}
58
59
/**
60
 * fu_x509_certificate_get_issuer:
61
 * @self: A #FuX509Certificate
62
 *
63
 * Returns the certificate issuer.
64
 *
65
 * Returns: string, or %NULL for unset
66
 *
67
 * Since: 2.0.9
68
 **/
69
const gchar *
70
fu_x509_certificate_get_issuer(FuX509Certificate *self)
71
0
{
72
0
  g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL);
73
0
  return self->issuer;
74
0
}
75
76
#ifdef HAVE_GNUTLS
77
static void
78
fu_x509_certificate_set_issuer(FuX509Certificate *self, const gchar *issuer)
79
{
80
  g_return_if_fail(FU_IS_X509_CERTIFICATE(self));
81
  if (g_strcmp0(issuer, self->issuer) == 0)
82
    return;
83
  g_free(self->issuer);
84
  self->issuer = g_strdup(issuer);
85
}
86
87
static void
88
fu_x509_certificate_set_subject(FuX509Certificate *self, const gchar *subject)
89
{
90
  g_return_if_fail(FU_IS_X509_CERTIFICATE(self));
91
  if (g_strcmp0(subject, self->subject) == 0)
92
    return;
93
  g_free(self->subject);
94
  self->subject = g_strdup(subject);
95
}
96
97
static void
98
fu_x509_certificate_set_activation_time(FuX509Certificate *self, gint64 activation_time)
99
{
100
  g_return_if_fail(FU_IS_X509_CERTIFICATE(self));
101
  if (self->activation_time != NULL)
102
    g_date_time_unref(self->activation_time);
103
  self->activation_time = g_date_time_new_from_unix_utc(activation_time);
104
}
105
#endif
106
107
/**
108
 * fu_x509_certificate_get_subject:
109
 * @self: A #FuX509Certificate
110
 *
111
 * Returns the certificate subject.
112
 *
113
 * Returns: string, or %NULL for unset
114
 *
115
 * Since: 2.0.9
116
 **/
117
const gchar *
118
fu_x509_certificate_get_subject(FuX509Certificate *self)
119
0
{
120
0
  g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL);
121
0
  return self->subject;
122
0
}
123
124
/**
125
 * fu_x509_certificate_get_activation_time:
126
 * @self: A #FuX509Certificate
127
 *
128
 * Returns the certificate activation time.
129
 *
130
 * Returns: (transfer full): a #GDateTime, or %NULL for unset
131
 *
132
 * Since: 2.0.11
133
 **/
134
GDateTime *
135
fu_x509_certificate_get_activation_time(FuX509Certificate *self)
136
0
{
137
0
  g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL);
138
0
  if (self->activation_time == NULL)
139
0
    return NULL;
140
0
  return g_date_time_ref(self->activation_time);
141
0
}
142
143
static gboolean
144
fu_x509_certificate_parse(FuFirmware *firmware,
145
        GInputStream *stream,
146
        FuFirmwareParseFlags flags,
147
        GError **error)
148
0
{
149
#ifdef HAVE_GNUTLS
150
  FuX509Certificate *self = FU_X509_CERTIFICATE(firmware);
151
  gchar buf[1024] = {'\0'};
152
  guchar key_id[32] = {'\0'};
153
  gsize key_idsz = sizeof(key_id);
154
  gnutls_datum_t d = {0};
155
  gnutls_x509_dn_t dn = {0x0};
156
  gint64 ts;
157
  gsize bufsz = sizeof(buf);
158
  int rc;
159
  g_auto(gnutls_x509_crt_t) crt = NULL;
160
  g_autoptr(gnutls_datum_t) subject = NULL;
161
  g_autoptr(GBytes) blob = NULL;
162
  g_autoptr(GString) key_idstr = g_string_new(NULL);
163
164
  /* parse certificate */
165
  blob = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error);
166
  if (blob == NULL)
167
    return FALSE;
168
  d.size = g_bytes_get_size(blob);
169
  d.data = (unsigned char *)g_bytes_get_data(blob, NULL);
170
171
  rc = gnutls_x509_crt_init(&crt);
172
  if (rc < 0) {
173
    g_set_error(error,
174
          FWUPD_ERROR,
175
          FWUPD_ERROR_INVALID_DATA,
176
          "crt_init: %s [%i]",
177
          gnutls_strerror(rc),
178
          rc);
179
    return FALSE;
180
  }
181
  if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM)
182
    gnutls_x509_crt_set_flags(crt, GNUTLS_X509_CRT_FLAG_IGNORE_SANITY);
183
  rc = gnutls_x509_crt_import(crt, &d, GNUTLS_X509_FMT_DER);
184
  if (rc < 0) {
185
    g_set_error(error,
186
          FWUPD_ERROR,
187
          FWUPD_ERROR_INVALID_DATA,
188
          "crt_import: %s [%i]",
189
          gnutls_strerror(rc),
190
          rc);
191
    return FALSE;
192
  }
193
194
  /* issuer */
195
  if (gnutls_x509_crt_get_issuer_dn(crt, buf, &bufsz) == GNUTLS_E_SUCCESS) {
196
    g_autofree gchar *str = fu_strsafe((const gchar *)buf, bufsz);
197
    fu_x509_certificate_set_issuer(self, str);
198
  }
199
200
  /* subject */
201
  subject = (gnutls_datum_t *)gnutls_malloc(sizeof(gnutls_datum_t));
202
  if (gnutls_x509_crt_get_subject(crt, &dn) == GNUTLS_E_SUCCESS) {
203
    g_autofree gchar *str = NULL;
204
    gnutls_x509_dn_get_str(dn, subject);
205
    str = fu_strsafe((const gchar *)subject->data, subject->size);
206
    fu_x509_certificate_set_subject(self, str);
207
  }
208
209
  /* activation_time */
210
  ts = (gint64)gnutls_x509_crt_get_activation_time(crt);
211
  if (ts == -1) {
212
    g_set_error_literal(error,
213
            FWUPD_ERROR,
214
            FWUPD_ERROR_INVALID_DATA,
215
            "failed to get activation time");
216
    return FALSE;
217
  }
218
  fu_x509_certificate_set_activation_time(self, ts);
219
220
  /* key ID */
221
  rc = gnutls_x509_crt_get_key_id(crt, 0, key_id, &key_idsz);
222
  if (rc < 0) {
223
    g_set_error(error,
224
          FWUPD_ERROR,
225
          FWUPD_ERROR_INVALID_DATA,
226
          "failed to get key ID: %s [%i]",
227
          gnutls_strerror(rc),
228
          rc);
229
    return FALSE;
230
  }
231
  for (guint i = 0; i < key_idsz; i++)
232
    g_string_append_printf(key_idstr, "%02x", key_id[i]);
233
  fu_firmware_set_id(firmware, key_idstr->str);
234
235
  /* success */
236
  return TRUE;
237
#else
238
0
  g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no GnuTLS support");
239
0
  return FALSE;
240
0
#endif
241
0
}
242
243
static void
244
fu_x509_certificate_init(FuX509Certificate *self)
245
0
{
246
0
}
247
248
static void
249
fu_x509_certificate_finalize(GObject *obj)
250
0
{
251
0
  FuX509Certificate *self = FU_X509_CERTIFICATE(obj);
252
0
  g_free(self->issuer);
253
0
  g_free(self->subject);
254
0
  if (self->activation_time != NULL)
255
0
    g_date_time_unref(self->activation_time);
256
0
  G_OBJECT_CLASS(fu_x509_certificate_parent_class)->finalize(obj);
257
0
}
258
259
static void
260
fu_x509_certificate_class_init(FuX509CertificateClass *klass)
261
0
{
262
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
263
0
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
264
0
  object_class->finalize = fu_x509_certificate_finalize;
265
0
  firmware_class->export = fu_x509_certificate_export;
266
0
  firmware_class->parse = fu_x509_certificate_parse;
267
0
}
268
269
/**
270
 * fu_x509_certificate_new:
271
 *
272
 * Creates a new #FuX509Certificate.
273
 *
274
 * Returns: (transfer full): object
275
 *
276
 * Since: 2.0.9
277
 **/
278
FuX509Certificate *
279
fu_x509_certificate_new(void)
280
0
{
281
0
  return g_object_new(FU_TYPE_X509_CERTIFICATE, NULL);
282
0
}