Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2010 Collabora Ltd
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU Lesser General Public License as
8 : : * published by the Free Software Foundation; either version 2.1 of
9 : : * the License, or (at your option) any later version.
10 : : *
11 : : * This program is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Author: Stef Walter <stefw@collabora.co.uk>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gcr-certificate-chain.h"
25 : :
26 : : #include "gcr-certificate.h"
27 : : #include "gcr-pkcs11-certificate.h"
28 : : #include "gcr-simple-certificate.h"
29 : : #include "gcr-trust.h"
30 : :
31 : : #include "gcr/gcr-enum-types.h"
32 : :
33 : : #include "egg/egg-error.h"
34 : :
35 : : /**
36 : : * GcrCertificateChain:
37 : : *
38 : : * Represents a chain of certificates, normally used to
39 : : * validate the trust in a certificate. An X.509 certificate chain has one
40 : : * endpoint certificate (the one for which trust is being verified) and then
41 : : * in turn the certificate that issued each previous certificate in the chain.
42 : : *
43 : : * This functionality is for building of certificate chains not for validating
44 : : * them. Use your favorite crypto library to validate trust in a certificate
45 : : * chain once its built.
46 : : *
47 : : * The order of certificates in the chain should be first the endpoint
48 : : * certificates and then the signing certificates.
49 : : *
50 : : * Create a new certificate chain with [ctor@CertificateChain.new] and then
51 : : * add the certificates with [method@CertificateChain.add].
52 : : *
53 : : * You can then use [method@CertificateChain.build] to build the remainder of
54 : : * the chain. This will lookup missing certificates in PKCS#11 modules and
55 : : * also check that each certificate in the chain is the signer of the previous
56 : : * one. If a trust anchor, pinned certificate, or self-signed certificate is
57 : : * found, then the chain is considered built. Any extra certificates are
58 : : * removed from the chain.
59 : : *
60 : : * Once the certificate chain has been built, you can access its status
61 : : * through [method@CertificateChain.get_status]. The status signifies whether
62 : : * the chain is anchored on a trust root, self-signed, incomplete etc. See
63 : : * [enum@CertificateChainStatus] for information on the various statuses.
64 : : *
65 : : * It's important to understand that the building of a certificate chain is
66 : : * merely the first step towards verifying trust in a certificate.
67 : : */
68 : :
69 : : /**
70 : : * GcrCertificateChainClass:
71 : : * @parent_class: The parent class
72 : : *
73 : : * The class for #GcrCertificateChain.
74 : : */
75 : :
76 : : enum {
77 : : PROP_0,
78 : : PROP_STATUS,
79 : : PROP_LENGTH,
80 : : };
81 : :
82 : : struct _GcrCertificateChainPrivate {
83 : : GPtrArray *certificates;
84 : : GcrCertificateChainStatus status;
85 : :
86 : : /* Used in build operation */
87 : : gchar *purpose;
88 : : gchar *peer;
89 : : guint flags;
90 : : };
91 : :
92 : : static GQuark Q_ORIGINAL_CERT = 0;
93 : : static GQuark Q_OPERATION_DATA = 0;
94 : :
95 [ + + + - : 127 : G_DEFINE_TYPE (GcrCertificateChain, gcr_certificate_chain, G_TYPE_OBJECT);
+ + ]
96 : :
97 : : /* -----------------------------------------------------------------------------
98 : : * INTERNAL
99 : : */
100 : :
101 : : static void
102 : 28 : free_chain_private (gpointer data)
103 : : {
104 : 28 : GcrCertificateChainPrivate *pv = data;
105 : 28 : g_ptr_array_unref (pv->certificates);
106 : 28 : g_free (pv->purpose);
107 : 28 : g_free (pv->peer);
108 : 28 : g_slice_free (GcrCertificateChainPrivate, pv);
109 : 28 : }
110 : :
111 : : static GcrCertificateChainPrivate*
112 : 28 : new_chain_private (void)
113 : : {
114 : 28 : GcrCertificateChainPrivate *pv = g_slice_new0 (GcrCertificateChainPrivate);
115 : 28 : pv->certificates = g_ptr_array_new_with_free_func (g_object_unref);
116 : 28 : return pv;
117 : : }
118 : :
119 : : static GcrCertificateChainPrivate*
120 : 13 : prep_chain_private (GcrCertificateChainPrivate *orig, const gchar *purpose,
121 : : const gchar *peer, guint flags)
122 : : {
123 : : GcrCertificateChainPrivate *pv;
124 : : GcrCertificate *certificate;
125 : : guint i;
126 : :
127 [ - + ]: 13 : g_assert (orig);
128 [ - + ]: 13 : g_assert (purpose);
129 : :
130 : 13 : pv = new_chain_private ();
131 [ + + ]: 32 : for (i = 0; i < orig->certificates->len; ++i) {
132 : 19 : certificate = g_ptr_array_index (orig->certificates, i);
133 : 19 : g_ptr_array_add (pv->certificates, g_object_ref (certificate));
134 : : }
135 : :
136 : 13 : pv->status = orig->status;
137 : 13 : pv->purpose = g_strdup (purpose);
138 : 13 : pv->peer = g_strdup (peer);
139 : 13 : pv->flags = flags;
140 : 13 : return pv;
141 : : }
142 : :
143 : : static GcrCertificateChainPrivate*
144 : 2 : prep_chain_private_thread_safe (GcrCertificateChainPrivate *orig, const gchar *purpose,
145 : : const gchar *peer, guint flags)
146 : : {
147 : : GcrCertificateChainPrivate *pv;
148 : : GcrCertificate *certificate, *safe;
149 : : gconstpointer der;
150 : : gsize n_der;
151 : : guint i;
152 : :
153 [ - + ]: 2 : g_assert (orig);
154 : :
155 : 2 : pv = prep_chain_private (orig, purpose, peer, flags);
156 : :
157 [ + + ]: 6 : for (i = 0; i < pv->certificates->len; ++i) {
158 : 4 : certificate = g_ptr_array_index (pv->certificates, i);
159 : :
160 : : /* We regard these types as thread safe */
161 [ - + + - : 4 : if (GCR_IS_SIMPLE_CERTIFICATE (certificate) ||
+ + + + ]
162 [ - + + - : 3 : GCR_IS_PKCS11_CERTIFICATE (certificate)) {
- + - + ]
163 : 1 : safe = g_object_ref (certificate);
164 : :
165 : : /* Otherwise copy the certificate data */
166 : : } else {
167 : 3 : der = gcr_certificate_get_der_data (certificate, &n_der);
168 [ - + ]: 3 : g_return_val_if_fail (der, NULL);
169 : 3 : safe = gcr_simple_certificate_new (der, n_der);
170 : :
171 : 3 : g_debug ("copying certificate so it's thread safe");
172 : :
173 : : /* Always set the original certificate onto the safe one */
174 : 3 : g_object_set_qdata_full (G_OBJECT (safe), Q_ORIGINAL_CERT,
175 : : g_object_ref (certificate), g_object_unref);
176 : : }
177 : :
178 : 4 : g_ptr_array_index (pv->certificates, i) = safe;
179 : 4 : g_object_unref (certificate);
180 : : }
181 : :
182 : 2 : return pv;
183 : : }
184 : :
185 : : static GcrCertificateChainPrivate*
186 : 10 : cleanup_chain_private (GcrCertificateChainPrivate *pv)
187 : : {
188 : : GcrCertificate *certificate, *orig;
189 : : guint i;
190 : :
191 [ + + ]: 24 : for (i = 0; i < pv->certificates->len; ++i) {
192 : 14 : certificate = g_ptr_array_index (pv->certificates, i);
193 : :
194 : : /* If there's an original certificate set, then replace it back */
195 : 14 : orig = g_object_get_qdata (G_OBJECT (certificate), Q_ORIGINAL_CERT);
196 [ + + ]: 14 : if (orig != NULL) {
197 : 2 : g_ptr_array_index (pv->certificates, i) = g_object_ref (orig);
198 : 2 : g_object_unref (certificate);
199 : : }
200 : : }
201 : :
202 : 10 : return pv;
203 : : }
204 : :
205 : : static GcrCertificate *
206 : 22 : pop_certificate (GPtrArray *certificates,
207 : : GcrCertificate *issued)
208 : : {
209 : : GcrCertificate *certificate;
210 : : gint i;
211 : :
212 [ + + ]: 23 : for (i = 0; i < certificates->len; i++) {
213 : 17 : certificate = certificates->pdata[i];
214 [ + + + + ]: 17 : if (!issued || gcr_certificate_is_issuer (issued, certificate)) {
215 : 16 : g_object_ref (certificate);
216 : 16 : g_ptr_array_remove_index_fast (certificates, i);
217 : 16 : return certificate;
218 : : }
219 : : }
220 : :
221 : 6 : return NULL;
222 : : }
223 : :
224 : : static gboolean
225 : 13 : perform_build_chain (GcrCertificateChainPrivate *pv, GCancellable *cancellable,
226 : : GError **rerror)
227 : : {
228 : 13 : GError *error = NULL;
229 : : GcrCertificate *certificate;
230 : : GcrCertificate *issued;
231 : : GPtrArray *input;
232 : : gboolean lookups;
233 : : gboolean ret;
234 : : guint length;
235 : : gchar *subject;
236 : :
237 [ - + ]: 13 : g_assert (pv);
238 [ - + ]: 13 : g_assert (pv->certificates);
239 : :
240 : 13 : pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
241 : 13 : lookups = !((pv->flags & GCR_CERTIFICATE_CHAIN_NO_LOOKUPS) == GCR_CERTIFICATE_CHAIN_NO_LOOKUPS);
242 : :
243 : : /* This chain is built */
244 [ + + ]: 13 : if (!pv->certificates->len) {
245 : 1 : g_debug ("empty certificate chain");
246 : 1 : return TRUE;
247 : : }
248 : :
249 : 12 : input = pv->certificates;
250 : 12 : pv->certificates = g_ptr_array_new_with_free_func (g_object_unref);
251 : :
252 : : /* First check for pinned certificates */
253 : 12 : certificate = pop_certificate (input, NULL);
254 : 12 : g_ptr_array_add (pv->certificates, certificate);
255 : :
256 : 12 : subject = gcr_certificate_get_subject_dn (certificate);
257 : 12 : g_debug ("first certificate: %s", subject);
258 : 12 : g_free (subject);
259 : :
260 [ + + + + ]: 12 : if (lookups && pv->peer) {
261 : 1 : ret = gcr_trust_is_certificate_pinned (certificate, pv->purpose,
262 : 1 : pv->peer, cancellable, &error);
263 [ - + - - ]: 1 : if (!ret && error) {
264 : 0 : g_debug ("failed to lookup pinned certificate: %s",
265 : : egg_error_message (error));
266 : 0 : g_propagate_error (rerror, error);
267 : 0 : g_ptr_array_unref (input);
268 : 0 : return FALSE;
269 : : }
270 : :
271 : : /*
272 : : * This is a pinned certificate and the rest of the chain
273 : : * is irrelevant, so truncate chain and consider built.
274 : : */
275 [ + - ]: 1 : if (ret) {
276 : 1 : g_debug ("found pinned certificate for peer '%s', truncating chain",
277 : : pv->peer);
278 : :
279 : 1 : g_ptr_array_unref (input);
280 : 1 : pv->status = GCR_CERTIFICATE_CHAIN_PINNED;
281 : 1 : return TRUE;
282 : : }
283 : : }
284 : :
285 : : /* The first certificate is always unconditionally in the chain */
286 [ + - ]: 13 : while (pv->status == GCR_CERTIFICATE_CHAIN_UNKNOWN) {
287 : 13 : issued = certificate;
288 : :
289 : : /* Stop the chain if previous was self-signed */
290 [ + + ]: 13 : if (gcr_certificate_is_issuer (certificate, certificate)) {
291 : 3 : g_debug ("found self-signed certificate");
292 : 3 : pv->status = GCR_CERTIFICATE_CHAIN_SELFSIGNED;
293 : 3 : break;
294 : : }
295 : :
296 : : /* Get the next certificate */
297 : 10 : certificate = pop_certificate (input, issued);
298 [ + + ]: 10 : if (certificate) {
299 : 4 : subject = gcr_certificate_get_subject_dn (certificate);
300 : 4 : g_debug ("next certificate: %s", subject);
301 : 4 : g_free (subject);
302 : :
303 : : /* No more in chain, try to lookup */
304 [ + + ]: 6 : } else if (lookups) {
305 : 5 : certificate = gcr_pkcs11_certificate_lookup_issuer (issued,
306 : : cancellable, &error);
307 [ + + ]: 5 : if (error != NULL) {
308 : 3 : g_debug ("failed to lookup issuer: %s", error->message);
309 : 3 : g_propagate_error (rerror, error);
310 : 3 : g_ptr_array_unref (input);
311 : 3 : return FALSE;
312 : :
313 [ + + ]: 2 : } else if (certificate) {
314 : 1 : subject = gcr_certificate_get_subject_dn (certificate);
315 : 1 : g_debug ("found issuer certificate: %s", subject);
316 : 1 : g_free (subject);
317 : :
318 : : } else {
319 : 1 : g_debug ("no issuer found");
320 : : }
321 : :
322 : : /* No more in chain, and can't lookup */
323 : : } else {
324 : 1 : g_debug ("no more certificates available, and no lookups");
325 : 1 : certificate = NULL;
326 : : }
327 : :
328 : : /* Stop the chain if nothing found */
329 [ + + ]: 7 : if (certificate == NULL) {
330 : 2 : g_debug ("chain is incomplete");
331 : 2 : pv->status = GCR_CERTIFICATE_CHAIN_INCOMPLETE;
332 : 2 : break;
333 : : }
334 : :
335 : 5 : g_ptr_array_add (pv->certificates, certificate);
336 : 5 : ++length;
337 : :
338 : : /* See if this certificate is an anchor */
339 [ + - ]: 5 : if (lookups) {
340 : 5 : ret = gcr_trust_is_certificate_anchored (certificate, pv->purpose,
341 : : cancellable, &error);
342 : :
343 [ + + - + ]: 5 : if (!ret && error) {
344 : 0 : g_debug ("failed to lookup anchored certificate: %s",
345 : : egg_error_message (error));
346 : 0 : g_propagate_error (rerror, error);
347 : 0 : g_ptr_array_unref (input);
348 : 0 : return FALSE;
349 : :
350 : : /* Stop the chain at the first anchor */
351 [ + + ]: 5 : } else if (ret) {
352 : 3 : g_debug ("found anchored certificate");
353 : 3 : pv->status = GCR_CERTIFICATE_CHAIN_ANCHORED;
354 : 3 : break;
355 : : }
356 : : }
357 : : }
358 : :
359 : : /* TODO: Need to check each certificate in the chain for distrusted */
360 : :
361 : 8 : g_ptr_array_unref (input);
362 : 8 : return TRUE;
363 : : }
364 : :
365 : : static void
366 : 2 : thread_build_chain (GTask *task, gpointer src_object, gpointer task_data,
367 : : GCancellable *cancellable)
368 : : {
369 : : GcrCertificateChainPrivate *pv;
370 : 2 : GError *error = NULL;
371 : :
372 : 2 : pv = g_object_get_qdata (G_OBJECT (task), Q_OPERATION_DATA);
373 [ - + ]: 2 : g_assert (pv);
374 : :
375 : 2 : g_debug ("building asynchronously in another thread");
376 : :
377 [ + + ]: 2 : if (perform_build_chain (pv, cancellable, &error)) {
378 : 1 : g_task_return_boolean (task, TRUE);
379 : : } else {
380 : 1 : g_task_return_error (task, g_steal_pointer (&error));
381 : 1 : g_clear_error (&error);
382 : : }
383 : 2 : }
384 : :
385 : : /* -----------------------------------------------------------------------------
386 : : * OBJECT
387 : : */
388 : :
389 : : static void
390 : 15 : gcr_certificate_chain_init (GcrCertificateChain *self)
391 : : {
392 : 15 : self->pv = new_chain_private ();
393 : 15 : }
394 : :
395 : : static void
396 : 15 : gcr_certificate_chain_dispose (GObject *obj)
397 : : {
398 : 15 : GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
399 : :
400 : 15 : g_ptr_array_set_size (self->pv->certificates, 0);
401 : 15 : self->pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
402 : :
403 : 15 : G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->dispose (obj);
404 : 15 : }
405 : :
406 : : static void
407 : 15 : gcr_certificate_chain_finalize (GObject *obj)
408 : : {
409 : 15 : GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
410 : :
411 : 15 : free_chain_private (self->pv);
412 : 15 : self->pv = NULL;
413 : :
414 : 15 : G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->finalize (obj);
415 : 15 : }
416 : :
417 : : static void
418 : 2 : gcr_certificate_chain_get_property (GObject *obj, guint prop_id, GValue *value,
419 : : GParamSpec *pspec)
420 : : {
421 : 2 : GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
422 : :
423 [ + + - ]: 2 : switch (prop_id) {
424 : 1 : case PROP_STATUS:
425 : 1 : g_value_set_enum (value, gcr_certificate_chain_get_status (self));
426 : 1 : break;
427 : 1 : case PROP_LENGTH:
428 : 1 : g_value_set_uint (value, gcr_certificate_chain_get_length (self));
429 : 1 : break;
430 : 0 : default:
431 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
432 : 0 : break;
433 : : }
434 : 2 : }
435 : :
436 : : static void
437 : 1 : gcr_certificate_chain_class_init (GcrCertificateChainClass *klass)
438 : : {
439 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
440 : :
441 : 1 : gcr_certificate_chain_parent_class = g_type_class_peek_parent (klass);
442 : :
443 : 1 : gobject_class->dispose = gcr_certificate_chain_dispose;
444 : 1 : gobject_class->finalize = gcr_certificate_chain_finalize;
445 : 1 : gobject_class->get_property = gcr_certificate_chain_get_property;
446 : :
447 : : /**
448 : : * GcrCertificateChain:status:
449 : : *
450 : : * The certificate chain status. See #GcrCertificateChainStatus
451 : : */
452 : 1 : g_object_class_install_property (gobject_class, PROP_STATUS,
453 : : g_param_spec_enum ("status", "Status", "Status of certificate chain",
454 : : GCR_TYPE_CERTIFICATE_CHAIN_STATUS,
455 : : GCR_CERTIFICATE_CHAIN_UNKNOWN,
456 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
457 : :
458 : : /**
459 : : * GcrCertificateChain:length:
460 : : *
461 : : * The length of the certificate chain.
462 : : */
463 : 1 : g_object_class_install_property (gobject_class, PROP_LENGTH,
464 : : g_param_spec_uint ("length", "Length", "Length of certificate chain",
465 : : 0, G_MAXUINT, 0,
466 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
467 : :
468 : 1 : Q_ORIGINAL_CERT = g_quark_from_static_string ("gcr-certificate-chain-original-cert");
469 : 1 : Q_OPERATION_DATA = g_quark_from_static_string ("gcr-certificate-chain-operation-data");
470 : 1 : }
471 : :
472 : : /* -----------------------------------------------------------------------------
473 : : * PUBLIC
474 : : */
475 : :
476 : : /**
477 : : * GcrCertificateChainStatus:
478 : : * @GCR_CERTIFICATE_CHAIN_UNKNOWN: The certificate chain's status is unknown.
479 : : * When a chain is not yet built it has this status. If a chain is modified after
480 : : * being built, it has this status.
481 : : * @GCR_CERTIFICATE_CHAIN_INCOMPLETE: A full chain could not be loaded. The
482 : : * chain does not end with a self-signed certificate, a trusted anchor, or a
483 : : * pinned certificate.
484 : : * @GCR_CERTIFICATE_CHAIN_SELFSIGNED: The chain ends with a self-signed
485 : : * certificate. No trust anchor was found.
486 : : * @GCR_CERTIFICATE_CHAIN_DISTRUSTED: The certificate chain contains a revoked
487 : : * or otherwise explicitly distrusted certificate. The entire chain should
488 : : * be distrusted.
489 : : * @GCR_CERTIFICATE_CHAIN_ANCHORED: The chain ends with an anchored
490 : : * certificate. The anchored certificate is not necessarily self-signed.
491 : : * @GCR_CERTIFICATE_CHAIN_PINNED: The chain represents a pinned certificate. A
492 : : * pinned certificate is an exception which trusts a given certificate
493 : : * explicitly for a purpose and communication with a certain peer.
494 : : *
495 : : * The status of a built certificate chain. Will be set to
496 : : * %GCR_CERTIFICATE_CHAIN_UNKNOWN for certificate chains that have not been
497 : : * built.
498 : : */
499 : :
500 : : /**
501 : : * GCR_TYPE_CERTIFICATE_CHAIN_STATUS:
502 : : *
503 : : * The enum #GType for #GcrCertificateChainStatus.
504 : : */
505 : :
506 : : /**
507 : : * GcrCertificateChainFlags:
508 : : * @GCR_CERTIFICATE_CHAIN_NONE: no flags
509 : : * @GCR_CERTIFICATE_CHAIN_NO_LOOKUPS: If this flag is specified then no
510 : : * lookups for anchors or pinned certificates are done, and the resulting chain
511 : : * will be neither anchored or pinned. Additionally no missing certificate
512 : : * authorities are looked up in PKCS#11.
513 : : *
514 : : * Flags to be used with the gcr_certificate_chain_build() operation.
515 : : */
516 : :
517 : : /**
518 : : * GCR_TYPE_CERTIFICATE_CHAIN_FLAGS:
519 : : *
520 : : * The flags #GType for #GcrCertificateChainFlags.
521 : : */
522 : :
523 : : /**
524 : : * gcr_certificate_chain_new:
525 : : *
526 : : * Create a new #GcrCertificateChain.
527 : : *
528 : : * Returns: (transfer full): a newly allocated certificate chain
529 : : */
530 : : GcrCertificateChain *
531 : 15 : gcr_certificate_chain_new (void)
532 : : {
533 : 15 : return g_object_new (GCR_TYPE_CERTIFICATE_CHAIN, NULL);
534 : : }
535 : :
536 : : /**
537 : : * gcr_certificate_chain_add:
538 : : * @self: the #GcrCertificateChain
539 : : * @certificate: a #GcrCertificate to add to the chain
540 : : *
541 : : * Add @certificate to the chain. The order of certificates in the chain are
542 : : * important. The first certificate should be the endpoint certificate, and
543 : : * then come the signers (certificate authorities) each in turn. If a root
544 : : * certificate authority is present, it should come last.
545 : : *
546 : : * Adding a certificate an already built chain (see
547 : : * gcr_certificate_chain_build()) resets the type of the certificate chain
548 : : * to %GCR_CERTIFICATE_CHAIN_UNKNOWN
549 : : */
550 : : void
551 : 21 : gcr_certificate_chain_add (GcrCertificateChain *self, GcrCertificate *certificate)
552 : : {
553 [ - + + - : 21 : g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
+ - - + ]
554 [ - + + - : 21 : g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
- + - + ]
555 : 21 : g_ptr_array_add (self->pv->certificates, g_object_ref (certificate));
556 : 21 : self->pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
557 : 21 : g_object_notify (G_OBJECT (self), "status");
558 : 21 : g_object_notify (G_OBJECT (self), "length");
559 : : }
560 : :
561 : : /**
562 : : * gcr_certificate_chain_get_status:
563 : : * @self: the #GcrCertificateChain
564 : : *
565 : : * Get the status of a certificate chain. If the certificate chain has not
566 : : * been built, then the status will be %GCR_CERTIFICATE_CHAIN_UNKNOWN.
567 : : *
568 : : * A status of %GCR_CERTIFICATE_CHAIN_ANCHORED does not mean that the
569 : : * certificate chain has been verified, but merely that an anchor has been
570 : : * found.
571 : : *
572 : : * Returns: the status of the certificate chain.
573 : : */
574 : : GcrCertificateChainStatus
575 : 16 : gcr_certificate_chain_get_status (GcrCertificateChain *self)
576 : : {
577 [ - + + - : 16 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), GCR_CERTIFICATE_CHAIN_UNKNOWN);
+ - - + ]
578 : 16 : return self->pv->status;
579 : : }
580 : :
581 : : /**
582 : : * gcr_certificate_chain_get_anchor:
583 : : * @self: the #GcrCertificateChain
584 : : *
585 : : * If the certificate chain has been built and is of status
586 : : * %GCR_CERTIFICATE_CHAIN_ANCHORED, then this will return the anchor
587 : : * certificate that was found. This is not necessarily a root certificate
588 : : * authority. If an intermediate certificate authority in the chain was
589 : : * found to be anchored, then that certificate will be returned.
590 : : *
591 : : * If an anchor is returned it does not mean that the certificate chain has
592 : : * been verified, but merely that an anchor has been found.
593 : : *
594 : : * Returns: (transfer none): the anchor certificate, or %NULL if not anchored.
595 : : */
596 : : GcrCertificate *
597 : 6 : gcr_certificate_chain_get_anchor (GcrCertificateChain *self)
598 : : {
599 [ - + + - : 6 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ - - + ]
600 [ + + ]: 6 : if (self->pv->status != GCR_CERTIFICATE_CHAIN_ANCHORED)
601 : 3 : return NULL;
602 [ - + ]: 3 : g_assert (self->pv->certificates->len > 0);
603 : 3 : return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates,
604 : : self->pv->certificates->len - 1));
605 : : }
606 : :
607 : : /**
608 : : * gcr_certificate_chain_get_endpoint:
609 : : * @self: the #GcrCertificateChain
610 : : *
611 : : * Get the endpoint certificate in the chain. This is always the first
612 : : * certificate in the chain. The endpoint certificate cannot be anchored.
613 : : *
614 : : * Returns: (transfer none): the endpoint certificate, or %NULL if the chain
615 : : * is empty
616 : : */
617 : : GcrCertificate*
618 : 2 : gcr_certificate_chain_get_endpoint (GcrCertificateChain *self)
619 : : {
620 [ - + + - : 2 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ - - + ]
621 [ + + ]: 2 : if (!self->pv->certificates->len)
622 : 1 : return NULL;
623 : 1 : return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, 0));
624 : : }
625 : :
626 : : /**
627 : : * gcr_certificate_chain_get_length:
628 : : * @self: the #GcrCertificateChain
629 : : *
630 : : * Get the length of the certificate chain.
631 : : *
632 : : * Returns: the length of the certificate chain
633 : : */
634 : : guint
635 : 17 : gcr_certificate_chain_get_length (GcrCertificateChain *self)
636 : : {
637 [ - + + - : 17 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), 0);
+ - - + ]
638 : 17 : return self->pv->certificates->len;
639 : : }
640 : :
641 : : /**
642 : : * gcr_certificate_chain_get_certificate:
643 : : * @self: the #GcrCertificateChain
644 : : * @index: index of the certificate to get
645 : : *
646 : : * Get a certificate in the chain. It is an error to call this function
647 : : * with an invalid index.
648 : : *
649 : : * Returns: (transfer none): the certificate
650 : : */
651 : : GcrCertificate *
652 : 1 : gcr_certificate_chain_get_certificate (GcrCertificateChain *self, guint index)
653 : : {
654 [ - + + - : 1 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
+ - - + ]
655 [ - + ]: 1 : g_return_val_if_fail (index < self->pv->certificates->len, NULL);
656 : 1 : return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, index));
657 : : }
658 : :
659 : : /**
660 : : * gcr_certificate_chain_build:
661 : : * @self: the #GcrCertificateChain
662 : : * @purpose: the purpose the certificate chain will be used for
663 : : * @peer: (nullable): the peer the certificate chain will be used with, or %NULL
664 : : * @flags: chain completion flags
665 : : * @cancellable: a #GCancellable or %NULL
666 : : * @error: a #GError or %NULL
667 : : *
668 : : * Complete a certificate chain. Once a certificate chain has been built
669 : : * its status can be examined.
670 : : *
671 : : * This operation will lookup missing certificates in PKCS#11
672 : : * modules and also that each certificate in the chain is the signer of the
673 : : * previous one. If a trust anchor, pinned certificate, or self-signed certificate
674 : : * is found, then the chain is considered built. Any extra certificates are
675 : : * removed from the chain.
676 : : *
677 : : * It's important to understand that building of a certificate chain does not
678 : : * constitute verifying that chain. This is merely the first step towards
679 : : * trust verification.
680 : : *
681 : : * The @purpose is a string like %GCR_PURPOSE_CLIENT_AUTH and is the purpose
682 : : * for which the certificate chain will be used. Trust anchors are looked up
683 : : * for this purpose. This argument is required.
684 : : *
685 : : * The @peer is usually the host name of the peer whith which this certificate
686 : : * chain is being used. It is used to look up pinned certificates that have
687 : : * been stored for this peer. If %NULL then no pinned certificates will
688 : : * be considered.
689 : : *
690 : : * If the %GCR_CERTIFICATE_CHAIN_NO_LOOKUPS flag is specified then no
691 : : * lookups for anchors or pinned certificates are done, and the resulting chain
692 : : * will be neither anchored or pinned. Additionally no missing certificate
693 : : * authorities are looked up in PKCS#11
694 : : *
695 : : * This call will block, see gcr_certificate_chain_build_async() for the
696 : : * asynchronous version.
697 : : *
698 : : * Returns: whether the operation completed successfully
699 : : */
700 : : gboolean
701 : 11 : gcr_certificate_chain_build (GcrCertificateChain *self,
702 : : const gchar *purpose,
703 : : const gchar *peer,
704 : : GcrCertificateChainFlags flags,
705 : : GCancellable *cancellable,
706 : : GError **error)
707 : : {
708 : : GcrCertificateChainPrivate *pv;
709 : : gboolean ret;
710 : :
711 [ - + + - : 11 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
+ - - + ]
712 [ - + ]: 11 : g_return_val_if_fail (purpose != NULL, FALSE);
713 : :
714 : 11 : pv = prep_chain_private (self->pv, purpose, peer, flags);
715 : :
716 : 11 : ret = perform_build_chain (pv, cancellable, error);
717 : :
718 [ + + ]: 11 : if (ret) {
719 : 9 : free_chain_private (self->pv);
720 : 9 : self->pv = cleanup_chain_private (pv);
721 : 9 : g_object_notify (G_OBJECT (self), "status");
722 : 9 : g_object_notify (G_OBJECT (self), "length");
723 : : } else {
724 : 2 : free_chain_private (pv);
725 : : }
726 : :
727 : 11 : return ret;
728 : : }
729 : :
730 : : /**
731 : : * gcr_certificate_chain_build_async:
732 : : * @self: the #GcrCertificateChain
733 : : * @purpose: the purpose the certificate chain will be used for
734 : : * @peer: (nullable): the peer the certificate chain will be used with, or %NULL
735 : : * @flags: chain completion flags
736 : : * @cancellable: a #GCancellable or %NULL
737 : : * @callback: this will be called when the operation completes.
738 : : * @user_data: data to pass to the callback
739 : : *
740 : : * Complete a certificate chain. Once a certificate chain has been built
741 : : * its status can be examined.
742 : : *
743 : : * This will lookup missing certificates in PKCS#11
744 : : * modules and also that each certificate in the chain is the signer of the
745 : : * previous one. If a trust anchor, pinned certificate, or self-signed certificate
746 : : * is found, then the chain is considered built. Any extra certificates are
747 : : * removed from the chain.
748 : : *
749 : : * It's important to understand that building of a certificate chain does not
750 : : * constitute verifying that chain. This is merely the first step towards
751 : : * trust verification.
752 : : *
753 : : * The @purpose is a string like %GCR_PURPOSE_CLIENT_AUTH and is the purpose
754 : : * for which the certificate chain will be used. Trust anchors are looked up
755 : : * for this purpose. This argument is required.
756 : : *
757 : : * The @peer is usually the host name of the peer whith which this certificate
758 : : * chain is being used. It is used to look up pinned certificates that have
759 : : * been stored for this peer. If %NULL then no pinned certificates will
760 : : * be considered.
761 : : *
762 : : * If the %GCR_CERTIFICATE_CHAIN_NO_LOOKUPS flag is specified then no
763 : : * lookups for anchors or pinned certificates are done, and the resulting chain
764 : : * will be neither anchored or pinned. Additionally no missing certificate
765 : : * authorities are looked up in PKCS#11
766 : : *
767 : : * When the operation is finished, @callback will be called. You can then call
768 : : * gcr_certificate_chain_build_finish() to get the result of the operation.
769 : : */
770 : : void
771 : 2 : gcr_certificate_chain_build_async (GcrCertificateChain *self,
772 : : const gchar *purpose,
773 : : const gchar *peer,
774 : : GcrCertificateChainFlags flags,
775 : : GCancellable *cancellable,
776 : : GAsyncReadyCallback callback,
777 : : gpointer user_data)
778 : : {
779 : : GcrCertificateChainPrivate *pv;
780 : : GTask *task;
781 : :
782 [ - + + - : 2 : g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
+ - - + ]
783 [ - + ]: 2 : g_return_if_fail (purpose);
784 : :
785 : 2 : pv = prep_chain_private_thread_safe (self->pv, purpose, peer, flags);
786 : :
787 : 2 : task = g_task_new (self, cancellable, callback, user_data);
788 [ + - ]: 2 : g_task_set_source_tag (task, gcr_certificate_chain_build_async);
789 : 2 : g_object_set_qdata_full (G_OBJECT (task), Q_OPERATION_DATA, pv, free_chain_private);
790 : :
791 : 2 : g_task_run_in_thread (task, thread_build_chain);
792 [ + - ]: 2 : g_clear_object (&task);
793 : : }
794 : :
795 : : /**
796 : : * gcr_certificate_chain_build_finish:
797 : : * @self: the #GcrCertificateChain
798 : : * @result: the #GAsyncResult passed to the callback
799 : : * @error: a #GError, or %NULL
800 : : *
801 : : * Finishes an asynchronous operation started by
802 : : * gcr_certificate_chain_build_async().
803 : : *
804 : : * Returns: whether the operation succeeded
805 : : */
806 : : gboolean
807 : 2 : gcr_certificate_chain_build_finish (GcrCertificateChain *self, GAsyncResult *result,
808 : : GError **error)
809 : : {
810 : : GcrCertificateChainPrivate *pv;
811 : :
812 [ - + + - : 2 : g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
+ - - + ]
813 [ - + ]: 2 : g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
814 : :
815 [ + + ]: 2 : if (!g_task_propagate_boolean (G_TASK (result), error))
816 : 1 : return FALSE;
817 : :
818 : 1 : pv = g_object_steal_qdata (G_OBJECT (result), Q_OPERATION_DATA);
819 [ - + ]: 1 : g_return_val_if_fail (pv, FALSE);
820 : :
821 : 1 : free_chain_private (self->pv);
822 : 1 : self->pv = cleanup_chain_private (pv);
823 : :
824 : 1 : g_object_notify (G_OBJECT (self), "status");
825 : 1 : g_object_notify (G_OBJECT (self), "length");
826 : 1 : return TRUE;
827 : : }
|