/*
* gnome-keyring
*
* Copyright (C) 2010 Collabora Ltd
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
*
* Author: Stef Walter
*/
#include "config.h"
#include "gcr-certificate-chain.h"
#include "gcr-certificate.h"
#include "gcr-pkcs11-certificate.h"
#include "gcr-simple-certificate.h"
#include "gcr-trust.h"
#include "gcr/gcr-enum-types-base.h"
#include "egg/egg-error.h"
/**
* SECTION:gcr-certificate-chain
* @title: GcrCertificateChain
* @short_description: A certificate chain
*
* #GcrCertificateChain represents a chain of certificates, normally used to
* validate the trust in a certificate. An X.509 certificate chain has one
* endpoint certificate (the one for which trust is being verified) and then
* in turn the certificate that issued each previous certificate in the chain.
*
* This functionality is for building of certificate chains not for validating
* them. Use your favorite crypto library to validate trust in a certificate
* chain once its built.
*
* The order of certificates in the chain should be first the endpoint
* certificates and then the signing certificates.
*
* Create a new certificate chain with gcr_certificate_chain_new() and then
* add the certificates with gcr_certificate_chain_add().
*
* You can then use gcr_certificate_chain_build() to build the remainder of
* the chain. This will lookup missing certificates in PKCS\#11 modules and
* also check that each certificate in the chain is the signer of the previous
* one. If a trust anchor, pinned certificate, or self-signed certificate is
* found, then the chain is considered built. Any extra certificates are
* removed from the chain.
*
* Once the certificate chain has been built, you can access its status
* through gcr_certificate_chain_get_status(). The status signifies whether
* the chain is anchored on a trust root, self-signed, incomplete etc. See
* #GcrCertificateChainStatus for information on the various statuses.
*
* It's important to understand that the building of a certificate chain is
* merely the first step towards verifying trust in a certificate.
*/
/**
* GcrCertificateChain:
*
* A chain of certificates.
*/
/**
* GcrCertificateChainClass:
* @parent_class: The parent class
*
* The class for #GcrCertificateChain.
*/
enum {
PROP_0,
PROP_STATUS,
PROP_LENGTH,
};
struct _GcrCertificateChainPrivate {
GPtrArray *certificates;
GcrCertificateChainStatus status;
/* Used in build operation */
gchar *purpose;
gchar *peer;
guint flags;
};
static GQuark Q_ORIGINAL_CERT = 0;
static GQuark Q_OPERATION_DATA = 0;
G_DEFINE_TYPE (GcrCertificateChain, gcr_certificate_chain, G_TYPE_OBJECT);
/* -----------------------------------------------------------------------------
* INTERNAL
*/
static void
free_chain_private (gpointer data)
{
GcrCertificateChainPrivate *pv = data;
g_ptr_array_unref (pv->certificates);
g_free (pv->purpose);
g_free (pv->peer);
g_slice_free (GcrCertificateChainPrivate, pv);
}
static GcrCertificateChainPrivate*
new_chain_private (void)
{
GcrCertificateChainPrivate *pv = g_slice_new0 (GcrCertificateChainPrivate);
pv->certificates = g_ptr_array_new_with_free_func (g_object_unref);
return pv;
}
static GcrCertificateChainPrivate*
prep_chain_private (GcrCertificateChainPrivate *orig, const gchar *purpose,
const gchar *peer, guint flags)
{
GcrCertificateChainPrivate *pv;
GcrCertificate *certificate;
guint i;
g_assert (orig);
g_assert (purpose);
pv = new_chain_private ();
for (i = 0; i < orig->certificates->len; ++i) {
certificate = g_ptr_array_index (orig->certificates, i);
g_ptr_array_add (pv->certificates, g_object_ref (certificate));
}
pv->status = orig->status;
pv->purpose = g_strdup (purpose);
pv->peer = g_strdup (peer);
pv->flags = flags;
return pv;
}
static GcrCertificateChainPrivate*
prep_chain_private_thread_safe (GcrCertificateChainPrivate *orig, const gchar *purpose,
const gchar *peer, guint flags)
{
GcrCertificateChainPrivate *pv;
GcrCertificate *certificate, *safe;
gconstpointer der;
gsize n_der;
guint i;
g_assert (orig);
pv = prep_chain_private (orig, purpose, peer, flags);
for (i = 0; i < pv->certificates->len; ++i) {
certificate = g_ptr_array_index (pv->certificates, i);
/* We regard these types as thread safe */
if (GCR_IS_SIMPLE_CERTIFICATE (certificate) ||
GCR_IS_PKCS11_CERTIFICATE (certificate)) {
safe = g_object_ref (certificate);
/* Otherwise copy the certificate data */
} else {
der = gcr_certificate_get_der_data (certificate, &n_der);
g_return_val_if_fail (der, NULL);
safe = gcr_simple_certificate_new (der, n_der);
g_debug ("copying certificate so it's thread safe");
/* Always set the original certificate onto the safe one */
g_object_set_qdata_full (G_OBJECT (safe), Q_ORIGINAL_CERT,
g_object_ref (certificate), g_object_unref);
}
g_ptr_array_index (pv->certificates, i) = safe;
g_object_unref (certificate);
}
return pv;
}
static GcrCertificateChainPrivate*
cleanup_chain_private (GcrCertificateChainPrivate *pv)
{
GcrCertificate *certificate, *orig;
guint i;
for (i = 0; i < pv->certificates->len; ++i) {
certificate = g_ptr_array_index (pv->certificates, i);
/* If there's an original certificate set, then replace it back */
orig = g_object_get_qdata (G_OBJECT (certificate), Q_ORIGINAL_CERT);
if (orig != NULL) {
g_ptr_array_index (pv->certificates, i) = g_object_ref (orig);
g_object_unref (certificate);
}
}
return pv;
}
static GcrCertificate *
pop_certificate (GPtrArray *certificates,
GcrCertificate *issued)
{
GcrCertificate *certificate;
gint i;
for (i = 0; i < certificates->len; i++) {
certificate = certificates->pdata[i];
if (!issued || gcr_certificate_is_issuer (issued, certificate)) {
g_object_ref (certificate);
g_ptr_array_remove_index_fast (certificates, i);
return certificate;
}
}
return NULL;
}
static gboolean
perform_build_chain (GcrCertificateChainPrivate *pv, GCancellable *cancellable,
GError **rerror)
{
GError *error = NULL;
GcrCertificate *certificate;
GcrCertificate *issued;
GPtrArray *input;
gboolean lookups;
gboolean ret;
guint length;
gchar *subject;
g_assert (pv);
g_assert (pv->certificates);
pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
lookups = !((pv->flags & GCR_CERTIFICATE_CHAIN_NO_LOOKUPS) == GCR_CERTIFICATE_CHAIN_NO_LOOKUPS);
/* This chain is built */
if (!pv->certificates->len) {
g_debug ("empty certificate chain");
return TRUE;
}
input = pv->certificates;
pv->certificates = g_ptr_array_new_with_free_func (g_object_unref);
/* First check for pinned certificates */
certificate = pop_certificate (input, NULL);
g_ptr_array_add (pv->certificates, certificate);
subject = gcr_certificate_get_subject_dn (certificate);
g_debug ("first certificate: %s", subject);
g_free (subject);
if (lookups && pv->peer) {
ret = gcr_trust_is_certificate_pinned (certificate, pv->purpose,
pv->peer, cancellable, &error);
if (!ret && error) {
g_debug ("failed to lookup pinned certificate: %s",
egg_error_message (error));
g_propagate_error (rerror, error);
g_ptr_array_unref (input);
return FALSE;
}
/*
* This is a pinned certificate and the rest of the chain
* is irrelevant, so truncate chain and consider built.
*/
if (ret) {
g_debug ("found pinned certificate for peer '%s', truncating chain",
pv->peer);
g_ptr_array_unref (input);
pv->status = GCR_CERTIFICATE_CHAIN_PINNED;
return TRUE;
}
}
/* The first certificate is always unconditionally in the chain */
while (pv->status == GCR_CERTIFICATE_CHAIN_UNKNOWN) {
issued = certificate;
/* Stop the chain if previous was self-signed */
if (gcr_certificate_is_issuer (certificate, certificate)) {
g_debug ("found self-signed certificate");
pv->status = GCR_CERTIFICATE_CHAIN_SELFSIGNED;
break;
}
/* Get the next certificate */
certificate = pop_certificate (input, issued);
if (certificate) {
subject = gcr_certificate_get_subject_dn (certificate);
g_debug ("next certificate: %s", subject);
g_free (subject);
/* No more in chain, try to lookup */
} else if (lookups) {
certificate = gcr_pkcs11_certificate_lookup_issuer (issued,
cancellable, &error);
if (error != NULL) {
g_debug ("failed to lookup issuer: %s", error->message);
g_propagate_error (rerror, error);
g_ptr_array_unref (input);
return FALSE;
} else if (certificate) {
subject = gcr_certificate_get_subject_dn (certificate);
g_debug ("found issuer certificate: %s", subject);
g_free (subject);
} else {
g_debug ("no issuer found");
}
/* No more in chain, and can't lookup */
} else {
g_debug ("no more certificates available, and no lookups");
certificate = NULL;
}
/* Stop the chain if nothing found */
if (certificate == NULL) {
g_debug ("chain is incomplete");
pv->status = GCR_CERTIFICATE_CHAIN_INCOMPLETE;
break;
}
g_ptr_array_add (pv->certificates, certificate);
++length;
/* See if this certificate is an anchor */
if (lookups) {
ret = gcr_trust_is_certificate_anchored (certificate, pv->purpose,
cancellable, &error);
if (!ret && error) {
g_debug ("failed to lookup anchored certificate: %s",
egg_error_message (error));
g_propagate_error (rerror, error);
g_ptr_array_unref (input);
return FALSE;
/* Stop the chain at the first anchor */
} else if (ret) {
g_debug ("found anchored certificate");
pv->status = GCR_CERTIFICATE_CHAIN_ANCHORED;
break;
}
}
}
/* TODO: Need to check each certificate in the chain for distrusted */
g_ptr_array_unref (input);
return TRUE;
}
static void
thread_build_chain (GTask *task, gpointer src_object, gpointer task_data,
GCancellable *cancellable)
{
GcrCertificateChainPrivate *pv;
GError *error = NULL;
pv = g_object_get_qdata (G_OBJECT (task), Q_OPERATION_DATA);
g_assert (pv);
g_debug ("building asynchronously in another thread");
if (perform_build_chain (pv, cancellable, &error)) {
g_task_return_boolean (task, TRUE);
} else {
g_task_return_error (task, g_steal_pointer (&error));
g_clear_error (&error);
}
}
/* -----------------------------------------------------------------------------
* OBJECT
*/
static void
gcr_certificate_chain_init (GcrCertificateChain *self)
{
self->pv = new_chain_private ();
}
static void
gcr_certificate_chain_dispose (GObject *obj)
{
GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
g_ptr_array_set_size (self->pv->certificates, 0);
self->pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->dispose (obj);
}
static void
gcr_certificate_chain_finalize (GObject *obj)
{
GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
free_chain_private (self->pv);
self->pv = NULL;
G_OBJECT_CLASS (gcr_certificate_chain_parent_class)->finalize (obj);
}
static void
gcr_certificate_chain_get_property (GObject *obj, guint prop_id, GValue *value,
GParamSpec *pspec)
{
GcrCertificateChain *self = GCR_CERTIFICATE_CHAIN (obj);
switch (prop_id) {
case PROP_STATUS:
g_value_set_enum (value, gcr_certificate_chain_get_status (self));
break;
case PROP_LENGTH:
g_value_set_uint (value, gcr_certificate_chain_get_length (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gcr_certificate_chain_class_init (GcrCertificateChainClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gcr_certificate_chain_parent_class = g_type_class_peek_parent (klass);
gobject_class->dispose = gcr_certificate_chain_dispose;
gobject_class->finalize = gcr_certificate_chain_finalize;
gobject_class->get_property = gcr_certificate_chain_get_property;
/**
* GcrCertificateChain:status:
*
* The certificate chain status. See #GcrCertificateChainStatus
*/
g_object_class_install_property (gobject_class, PROP_STATUS,
g_param_spec_enum ("status", "Status", "Status of certificate chain",
GCR_TYPE_CERTIFICATE_CHAIN_STATUS,
GCR_CERTIFICATE_CHAIN_UNKNOWN,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GcrCertificateChain:length:
*
* The length of the certificate chain.
*/
g_object_class_install_property (gobject_class, PROP_LENGTH,
g_param_spec_uint ("length", "Length", "Length of certificate chain",
0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Q_ORIGINAL_CERT = g_quark_from_static_string ("gcr-certificate-chain-original-cert");
Q_OPERATION_DATA = g_quark_from_static_string ("gcr-certificate-chain-operation-data");
}
/* -----------------------------------------------------------------------------
* PUBLIC
*/
/**
* GcrCertificateChainStatus:
* @GCR_CERTIFICATE_CHAIN_UNKNOWN: The certificate chain's status is unknown.
* When a chain is not yet built it has this status. If a chain is modified after
* being built, it has this status.
* @GCR_CERTIFICATE_CHAIN_INCOMPLETE: A full chain could not be loaded. The
* chain does not end with a self-signed certificate, a trusted anchor, or a
* pinned certificate.
* @GCR_CERTIFICATE_CHAIN_SELFSIGNED: The chain ends with a self-signed
* certificate. No trust anchor was found.
* @GCR_CERTIFICATE_CHAIN_DISTRUSTED: The certificate chain contains a revoked
* or otherwise explicitly distrusted certificate. The entire chain should
* be distrusted.
* @GCR_CERTIFICATE_CHAIN_ANCHORED: The chain ends with an anchored
* certificate. The anchored certificate is not necessarily self-signed.
* @GCR_CERTIFICATE_CHAIN_PINNED: The chain represents a pinned certificate. A
* pinned certificate is an exception which trusts a given certificate
* explicitly for a purpose and communication with a certain peer.
*
* The status of a built certificate chain. Will be set to
* %GCR_CERTIFICATE_CHAIN_UNKNOWN for certificate chains that have not been
* built.
*/
/**
* GCR_TYPE_CERTIFICATE_CHAIN_STATUS:
*
* The enum #GType for #GcrCertificateChainStatus.
*/
/**
* GcrCertificateChainFlags:
* @GCR_CERTIFICATE_CHAIN_NONE: no flags
* @GCR_CERTIFICATE_CHAIN_NO_LOOKUPS: If this flag is specified then no
* lookups for anchors or pinned certificates are done, and the resulting chain
* will be neither anchored or pinned. Additionally no missing certificate
* authorities are looked up in PKCS\#11.
*
* Flags to be used with the gcr_certificate_chain_build() operation.
*/
/**
* GCR_TYPE_CERTIFICATE_CHAIN_FLAGS:
*
* The flags #GType for #GcrCertificateChainFlags.
*/
/**
* gcr_certificate_chain_new:
*
* Create a new #GcrCertificateChain.
*
* Returns: (transfer full): a newly allocated certificate chain
*/
GcrCertificateChain *
gcr_certificate_chain_new (void)
{
return g_object_new (GCR_TYPE_CERTIFICATE_CHAIN, NULL);
}
/**
* gcr_certificate_chain_add:
* @self: the #GcrCertificateChain
* @certificate: a #GcrCertificate to add to the chain
*
* Add @certificate to the chain. The order of certificates in the chain are
* important. The first certificate should be the endpoint certificate, and
* then come the signers (certificate authorities) each in turn. If a root
* certificate authority is present, it should come last.
*
* Adding a certificate an already built chain (see
* gcr_certificate_chain_build()) resets the type of the certificate chain
* to %GCR_CERTIFICATE_CHAIN_UNKNOWN
*/
void
gcr_certificate_chain_add (GcrCertificateChain *self, GcrCertificate *certificate)
{
g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
g_ptr_array_add (self->pv->certificates, g_object_ref (certificate));
self->pv->status = GCR_CERTIFICATE_CHAIN_UNKNOWN;
g_object_notify (G_OBJECT (self), "status");
g_object_notify (G_OBJECT (self), "length");
}
/**
* gcr_certificate_chain_get_status:
* @self: the #GcrCertificateChain
*
* Get the status of a certificate chain. If the certificate chain has not
* been built, then the status will be %GCR_CERTIFICATE_CHAIN_UNKNOWN.
*
* A status of %GCR_CERTIFICATE_CHAIN_ANCHORED does not mean that the
* certificate chain has been verified, but merely that an anchor has been
* found.
*
* Returns: the status of the certificate chain.
*/
GcrCertificateChainStatus
gcr_certificate_chain_get_status (GcrCertificateChain *self)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), GCR_CERTIFICATE_CHAIN_UNKNOWN);
return self->pv->status;
}
/**
* gcr_certificate_chain_get_anchor:
* @self: the #GcrCertificateChain
*
* If the certificate chain has been built and is of status
* %GCR_CERTIFICATE_CHAIN_ANCHORED, then this will return the anchor
* certificate that was found. This is not necessarily a root certificate
* authority. If an intermediate certificate authority in the chain was
* found to be anchored, then that certificate will be returned.
*
* If an anchor is returned it does not mean that the certificate chain has
* been verified, but merely that an anchor has been found.
*
* Returns: (transfer none): the anchor certificate, or %NULL if not anchored.
*/
GcrCertificate *
gcr_certificate_chain_get_anchor (GcrCertificateChain *self)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
if (self->pv->status != GCR_CERTIFICATE_CHAIN_ANCHORED)
return NULL;
g_assert (self->pv->certificates->len > 0);
return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates,
self->pv->certificates->len - 1));
}
/**
* gcr_certificate_chain_get_endpoint:
* @self: the #GcrCertificateChain
*
* Get the endpoint certificate in the chain. This is always the first
* certificate in the chain. The endpoint certificate cannot be anchored.
*
* Returns: (transfer none): the endpoint certificate, or %NULL if the chain
* is empty
*/
GcrCertificate*
gcr_certificate_chain_get_endpoint (GcrCertificateChain *self)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
if (!self->pv->certificates->len)
return NULL;
return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, 0));
}
/**
* gcr_certificate_chain_get_length:
* @self: the #GcrCertificateChain
*
* Get the length of the certificate chain.
*
* Returns: the length of the certificate chain
*/
guint
gcr_certificate_chain_get_length (GcrCertificateChain *self)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), 0);
return self->pv->certificates->len;
}
/**
* gcr_certificate_chain_get_certificate:
* @self: the #GcrCertificateChain
* @index: index of the certificate to get
*
* Get a certificate in the chain. It is an error to call this function
* with an invalid index.
*
* Returns: (transfer none): the certificate
*/
GcrCertificate *
gcr_certificate_chain_get_certificate (GcrCertificateChain *self, guint index)
{
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), NULL);
g_return_val_if_fail (index < self->pv->certificates->len, NULL);
return GCR_CERTIFICATE (g_ptr_array_index (self->pv->certificates, index));
}
/**
* gcr_certificate_chain_build:
* @self: the #GcrCertificateChain
* @purpose: the purpose the certificate chain will be used for
* @peer: (nullable): the peer the certificate chain will be used with, or %NULL
* @flags: chain completion flags
* @cancellable: a #GCancellable or %NULL
* @error: a #GError or %NULL
*
* Complete a certificate chain. Once a certificate chain has been built
* its status can be examined.
*
* This operation will lookup missing certificates in PKCS\#11
* modules and also that each certificate in the chain is the signer of the
* previous one. If a trust anchor, pinned certificate, or self-signed certificate
* is found, then the chain is considered built. Any extra certificates are
* removed from the chain.
*
* It's important to understand that building of a certificate chain does not
* constitute verifying that chain. This is merely the first step towards
* trust verification.
*
* The @purpose is a string like %GCR_PURPOSE_CLIENT_AUTH and is the purpose
* for which the certificate chain will be used. Trust anchors are looked up
* for this purpose. This argument is required.
*
* The @peer is usually the host name of the peer whith which this certificate
* chain is being used. It is used to look up pinned certificates that have
* been stored for this peer. If %NULL then no pinned certificates will
* be considered.
*
* If the %GCR_CERTIFICATE_CHAIN_NO_LOOKUPS flag is specified then no
* lookups for anchors or pinned certificates are done, and the resulting chain
* will be neither anchored or pinned. Additionally no missing certificate
* authorities are looked up in PKCS\#11
*
* This call will block, see gcr_certificate_chain_build_async() for the
* asynchronous version.
*
* Returns: whether the operation completed successfully
*/
gboolean
gcr_certificate_chain_build (GcrCertificateChain *self,
const gchar *purpose,
const gchar *peer,
GcrCertificateChainFlags flags,
GCancellable *cancellable,
GError **error)
{
GcrCertificateChainPrivate *pv;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
g_return_val_if_fail (purpose != NULL, FALSE);
pv = prep_chain_private (self->pv, purpose, peer, flags);
ret = perform_build_chain (pv, cancellable, error);
if (ret) {
free_chain_private (self->pv);
self->pv = cleanup_chain_private (pv);
g_object_notify (G_OBJECT (self), "status");
g_object_notify (G_OBJECT (self), "length");
} else {
free_chain_private (pv);
}
return ret;
}
/**
* gcr_certificate_chain_build_async:
* @self: the #GcrCertificateChain
* @purpose: the purpose the certificate chain will be used for
* @peer: (nullable): the peer the certificate chain will be used with, or %NULL
* @flags: chain completion flags
* @cancellable: a #GCancellable or %NULL
* @callback: this will be called when the operation completes.
* @user_data: data to pass to the callback
*
* Complete a certificate chain. Once a certificate chain has been built
* its status can be examined.
*
* This will lookup missing certificates in PKCS\#11
* modules and also that each certificate in the chain is the signer of the
* previous one. If a trust anchor, pinned certificate, or self-signed certificate
* is found, then the chain is considered built. Any extra certificates are
* removed from the chain.
*
* It's important to understand that building of a certificate chain does not
* constitute verifying that chain. This is merely the first step towards
* trust verification.
*
* The @purpose is a string like %GCR_PURPOSE_CLIENT_AUTH and is the purpose
* for which the certificate chain will be used. Trust anchors are looked up
* for this purpose. This argument is required.
*
* The @peer is usually the host name of the peer whith which this certificate
* chain is being used. It is used to look up pinned certificates that have
* been stored for this peer. If %NULL then no pinned certificates will
* be considered.
*
* If the %GCR_CERTIFICATE_CHAIN_NO_LOOKUPS flag is specified then no
* lookups for anchors or pinned certificates are done, and the resulting chain
* will be neither anchored or pinned. Additionally no missing certificate
* authorities are looked up in PKCS\#11
*
* When the operation is finished, @callback will be called. You can then call
* gcr_certificate_chain_build_finish() to get the result of the operation.
*/
void
gcr_certificate_chain_build_async (GcrCertificateChain *self,
const gchar *purpose,
const gchar *peer,
GcrCertificateChainFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GcrCertificateChainPrivate *pv;
GTask *task;
g_return_if_fail (GCR_IS_CERTIFICATE_CHAIN (self));
g_return_if_fail (purpose);
pv = prep_chain_private_thread_safe (self->pv, purpose, peer, flags);
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_certificate_chain_build_async);
g_object_set_qdata_full (G_OBJECT (task), Q_OPERATION_DATA, pv, free_chain_private);
g_task_run_in_thread (task, thread_build_chain);
g_clear_object (&task);
}
/**
* gcr_certificate_chain_build_finish:
* @self: the #GcrCertificateChain
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_certificate_chain_build_async().
*
* Returns: whether the operation succeeded
*/
gboolean
gcr_certificate_chain_build_finish (GcrCertificateChain *self, GAsyncResult *result,
GError **error)
{
GcrCertificateChainPrivate *pv;
g_return_val_if_fail (GCR_IS_CERTIFICATE_CHAIN (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
if (!g_task_propagate_boolean (G_TASK (result), error))
return FALSE;
pv = g_object_steal_qdata (G_OBJECT (result), Q_OPERATION_DATA);
g_return_val_if_fail (pv, FALSE);
free_chain_private (self->pv);
self->pv = cleanup_chain_private (pv);
g_object_notify (G_OBJECT (self), "status");
g_object_notify (G_OBJECT (self), "length");
return TRUE;
}