/*
* 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-types.h"
#include "gcr-internal.h"
#include "gcr-library.h"
#include "gcr-trust.h"
#include "gck/gck.h"
#include "gck/pkcs11n.h"
#include "gck/pkcs11i.h"
#include "gck/pkcs11x.h"
#include
/**
* GCR_PURPOSE_SERVER_AUTH:
*
* The purpose used to verify the server certificate in a TLS connection. This
* is the most common purpose in use.
*/
/**
* GCR_PURPOSE_CLIENT_AUTH:
*
* The purpose used to verify the client certificate in a TLS connection.
*/
/**
* GCR_PURPOSE_CODE_SIGNING:
*
* The purpose used to verify certificate used for the signature on signed code.
*/
/**
* GCR_PURPOSE_EMAIL:
*
* The purpose used to verify certificates that are used in email communication
* such as S/MIME.
*/
/* ----------------------------------------------------------------------------------
* HELPERS
*/
static void
prepare_trust_attrs (GcrCertificate *certificate,
CK_X_ASSERTION_TYPE type,
GckBuilder *builder)
{
gconstpointer data;
gsize n_data;
gck_builder_add_ulong (builder, CKA_CLASS, CKO_X_TRUST_ASSERTION);
gck_builder_add_ulong (builder, CKA_X_ASSERTION_TYPE, type);
data = gcr_certificate_get_der_data (certificate, &n_data);
g_return_if_fail (data);
gck_builder_add_data (builder, CKA_X_CERTIFICATE_VALUE, data, n_data);
}
/* ----------------------------------------------------------------------------------
* GET PINNED CERTIFICATE
*/
static GckAttributes *
prepare_is_certificate_pinned (GcrCertificate *certificate, const gchar *purpose, const gchar *peer)
{
GckBuilder builder = GCK_BUILDER_INIT;
prepare_trust_attrs (certificate, CKT_X_PINNED_CERTIFICATE, &builder);
gck_builder_add_string (&builder, CKA_X_PURPOSE, purpose);
gck_builder_add_string (&builder, CKA_X_PEER, peer);
return gck_builder_end (&builder);
}
static gboolean
perform_is_certificate_pinned (GckAttributes *search,
GCancellable *cancellable,
GError **error)
{
GckEnumerator *en;
GList *slots;
GckObject *object;
if (!gcr_pkcs11_initialize (cancellable, error))
return FALSE;
slots = gcr_pkcs11_get_trust_lookup_slots ();
g_debug ("searching for pinned certificate in %d slots",
g_list_length (slots));
en = gck_slots_enumerate_objects (slots, search, 0);
g_clear_list (&slots, g_object_unref);
object = gck_enumerator_next (en, cancellable, error);
g_object_unref (en);
if (object)
g_object_unref (object);
g_debug ("%s certificate anchor", object ? "found" : "did not find");
return (object != NULL);
}
/**
* gcr_trust_is_certificate_pinned:
* @certificate: a #GcrCertificate to check
* @purpose: the purpose string
* @peer: the peer for this pinned
* @cancellable: a #GCancellable
* @error: a #GError, or %NULL
*
* Check if @certificate is pinned for @purpose to communicate with @peer.
* A pinned certificate overrides all other certificate verification.
*
* This call may block, see gcr_trust_is_certificate_pinned_async() for the
* non-blocking version.
*
* In the case of an error, %FALSE is also returned. Check @error to detect
* if an error occurred.
*
* Returns: %TRUE if the certificate is pinned for the host and purpose
*/
gboolean
gcr_trust_is_certificate_pinned (GcrCertificate *certificate, const gchar *purpose,
const gchar *peer, GCancellable *cancellable, GError **error)
{
GckAttributes *search;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), FALSE);
g_return_val_if_fail (purpose, FALSE);
g_return_val_if_fail (peer, FALSE);
search = prepare_is_certificate_pinned (certificate, purpose, peer);
g_return_val_if_fail (search, FALSE);
ret = perform_is_certificate_pinned (search, cancellable, error);
gck_attributes_unref (search);
return ret;
}
static void
thread_is_certificate_pinned (GTask *task, gpointer object,
gpointer task_data, GCancellable *cancellable)
{
GckAttributes *attrs = task_data;
GError *error = NULL;
gboolean found;
found = perform_is_certificate_pinned (attrs, cancellable, &error);
if (error == NULL)
g_task_return_boolean (task, found);
else
g_task_return_error (task, g_steal_pointer (&error));
}
/**
* gcr_trust_is_certificate_pinned_async:
* @certificate: a #GcrCertificate to check
* @purpose: the purpose string
* @peer: the peer for this pinned
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Check if @certificate is pinned for @purpose to communicate with @peer. A
* pinned certificate overrides all other certificate verification.
*
* When the operation is finished, callback will be called. You can then call
* [func@Gcr.trust_is_certificate_pinned_finish] to get the result of the
* operation.
*/
void
gcr_trust_is_certificate_pinned_async (GcrCertificate *certificate, const gchar *purpose,
const gchar *peer, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer user_data)
{
GTask *task;
GckAttributes *attrs;
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
g_return_if_fail (purpose);
g_return_if_fail (peer);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_trust_is_certificate_pinned_async);
attrs = prepare_is_certificate_pinned (certificate, purpose, peer);
g_return_if_fail (attrs);
g_task_set_task_data (task, attrs, gck_attributes_unref);
g_task_run_in_thread (task, thread_is_certificate_pinned);
g_clear_object (&task);
}
/**
* gcr_trust_is_certificate_pinned_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_trust_is_certificate_pinned_async().
*
* In the case of an error, %FALSE is also returned. Check @error to detect
* if an error occurred.
*
* Returns: %TRUE if the certificate is pinned.
*/
gboolean
gcr_trust_is_certificate_pinned_finish (GAsyncResult *result, GError **error)
{
g_return_val_if_fail (!error || !*error, FALSE);
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* ----------------------------------------------------------------------------------
* ADD PINNED CERTIFICATE
*/
static GckAttributes *
prepare_add_pinned_certificate (GcrCertificate *certificate, const gchar *purpose, const gchar *peer)
{
GckBuilder builder = GCK_BUILDER_INIT;
prepare_trust_attrs (certificate, CKT_X_PINNED_CERTIFICATE, &builder);
gck_builder_add_string (&builder, CKA_X_PURPOSE, purpose);
gck_builder_add_string (&builder, CKA_X_PEER, peer);
gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
return gck_builder_end (&builder);
}
static gboolean
perform_add_pinned_certificate (GckAttributes *search,
GCancellable *cancellable,
GError **error)
{
GckBuilder builder = GCK_BUILDER_INIT;
gboolean ret = FALSE;
GError *lerr = NULL;
GckObject *object;
GckSession *session;
GckSlot *slot;
GckEnumerator *en;
GList *slots;
if (!gcr_pkcs11_initialize (cancellable, error))
return FALSE;
slots = gcr_pkcs11_get_trust_lookup_slots ();
en = gck_slots_enumerate_objects (slots, search, CKF_RW_SESSION);
g_clear_list (&slots, g_object_unref);
object = gck_enumerator_next (en, cancellable, &lerr);
g_object_unref (en);
if (lerr != NULL) {
g_propagate_error (error, lerr);
return FALSE;
}
/* It already exists */
if (object) {
g_object_unref (object);
return TRUE;
}
gck_builder_add_all (&builder, search);
/* TODO: Add relevant label */
/* Find an appropriate token */
slot = gcr_pkcs11_get_trust_store_slot ();
if (slot == NULL) {
g_set_error (&lerr, GCK_ERROR, CKR_FUNCTION_FAILED,
/* Translators: A pinned certificate is an exception which
trusts a given certificate explicitly for a purpose and
communication with a certain peer. */
_("Couldn’t find a place to store the pinned certificate"));
ret = FALSE;
} else {
session = gck_slot_open_session (slot, CKF_RW_SESSION, NULL, NULL, &lerr);
if (session != NULL) {
GckAttributes *attrs = gck_builder_end (&builder);
object = gck_session_create_object (session, attrs,
cancellable, &lerr);
if (object != NULL) {
g_object_unref (object);
ret = TRUE;
}
g_object_unref (session);
gck_attributes_unref (attrs);
}
g_object_unref (slot);
}
gck_builder_clear (&builder);
if (!ret)
g_propagate_error (error, lerr);
return ret;
}
/**
* gcr_trust_add_pinned_certificate:
* @certificate: a #GcrCertificate
* @purpose: the purpose string
* @peer: the peer for this pinned certificate
* @cancellable: a #GCancellable
* @error: a #GError, or %NULL
*
* Add a pinned @certificate for connections to @peer for @purpose. A pinned
* certificate overrides all other certificate verification and should be
* used with care.
*
* If the same pinned certificate already exists, then this operation
* does not add another, and succeeds without error.
*
* This call may block, see gcr_trust_add_pinned_certificate_async() for the
* non-blocking version.
*
* Returns: %TRUE if the pinned certificate is recorded successfully
*/
gboolean
gcr_trust_add_pinned_certificate (GcrCertificate *certificate, const gchar *purpose, const gchar *peer,
GCancellable *cancellable, GError **error)
{
GckAttributes *search;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), FALSE);
g_return_val_if_fail (purpose, FALSE);
g_return_val_if_fail (peer, FALSE);
search = prepare_add_pinned_certificate (certificate, purpose, peer);
g_return_val_if_fail (search, FALSE);
ret = perform_add_pinned_certificate (search, cancellable, error);
gck_attributes_unref (search);
return ret;
}
static void
thread_add_pinned_certificate (GTask *task, gpointer object,
gpointer task_data, GCancellable *cancellable)
{
GckAttributes *attrs = task_data;
GError *error = NULL;
perform_add_pinned_certificate (attrs, cancellable, &error);
if (error == NULL)
g_task_return_boolean (task, TRUE);
else
g_task_return_error (task, g_steal_pointer (&error));
}
/**
* gcr_trust_add_pinned_certificate_async:
* @certificate: a #GcrCertificate
* @purpose: the purpose string
* @peer: the peer for this pinned certificate
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Add a pinned certificate for communication with @peer for @purpose. A pinned
* certificate overrides all other certificate verification and should be used
* with care.
*
* If the same pinned certificate already exists, then this operation
* does not add another, and succeeds without error.
*
* When the operation is finished, callback will be called. You can then call
* [func@Gcr.trust_add_pinned_certificate_finish] to get the result of the
* operation.
*/
void
gcr_trust_add_pinned_certificate_async (GcrCertificate *certificate, const gchar *purpose,
const gchar *peer, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer user_data)
{
GTask *task;
GckAttributes *attrs;
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
g_return_if_fail (purpose);
g_return_if_fail (peer);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_trust_add_pinned_certificate_async);
attrs = prepare_add_pinned_certificate (certificate, purpose, peer);
g_return_if_fail (attrs);
g_task_set_task_data (task, attrs, gck_attributes_unref);
g_task_run_in_thread (task, thread_add_pinned_certificate);
g_clear_object (&task);
}
/**
* gcr_trust_add_pinned_certificate_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_trust_add_pinned_certificate_async().
*
* Returns: %TRUE if the pinned certificate is recorded successfully
*/
gboolean
gcr_trust_add_pinned_certificate_finish (GAsyncResult *result, GError **error)
{
g_return_val_if_fail (!error || !*error, FALSE);
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* -----------------------------------------------------------------------
* REMOVE PINNED CERTIFICATE
*/
static GckAttributes *
prepare_remove_pinned_certificate (GcrCertificate *certificate, const gchar *purpose,
const gchar *peer)
{
GckBuilder builder = GCK_BUILDER_INIT;
prepare_trust_attrs (certificate, CKT_X_PINNED_CERTIFICATE, &builder);
gck_builder_add_string (&builder, CKA_X_PURPOSE, purpose);
gck_builder_add_string (&builder, CKA_X_PEER, peer);
return gck_builder_end (&builder);
}
static gboolean
perform_remove_pinned_certificate (GckAttributes *attrs,
GCancellable *cancellable,
GError **error)
{
GList *objects, *l;
GError *lerr = NULL;
GckEnumerator *en;
GList *slots;
if (!gcr_pkcs11_initialize (cancellable, error))
return FALSE;
slots = gcr_pkcs11_get_trust_lookup_slots ();
en = gck_slots_enumerate_objects (slots, attrs, CKF_RW_SESSION);
g_clear_list (&slots, g_object_unref);
/* We need an error below */
if (error && !*error)
*error = lerr;
objects = gck_enumerator_next_n (en, -1, cancellable, error);
g_object_unref (en);
if (*error)
return FALSE;
for (l = objects; l; l = g_list_next (l)) {
if (!gck_object_destroy (l->data, cancellable, error)) {
/* In case there's a race condition */
if (g_error_matches (*error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID)) {
g_clear_error (error);
continue;
}
g_clear_list (&objects, g_object_unref);
return FALSE;
}
}
g_clear_list (&objects, g_object_unref);
return TRUE;
}
/**
* gcr_trust_remove_pinned_certificate:
* @certificate: a #GcrCertificate
* @purpose: the purpose string
* @peer: the peer for this pinned certificate
* @cancellable: a #GCancellable
* @error: a #GError, or %NULL
*
* Remove a pinned certificate for communication with @peer for @purpose.
*
* If the same pinned certificate does not exist, or was already removed,
* then this operation succeeds without error.
*
* This call may block, see gcr_trust_remove_pinned_certificate_async() for the
* non-blocking version.
*
* Returns: %TRUE if the pinned certificate no longer exists
*/
gboolean
gcr_trust_remove_pinned_certificate (GcrCertificate *certificate, const gchar *purpose, const gchar *peer,
GCancellable *cancellable, GError **error)
{
GckAttributes *search;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), FALSE);
g_return_val_if_fail (purpose, FALSE);
g_return_val_if_fail (peer, FALSE);
search = prepare_remove_pinned_certificate (certificate, purpose, peer);
g_return_val_if_fail (search, FALSE);
ret = perform_remove_pinned_certificate (search, cancellable, error);
gck_attributes_unref (search);
return ret;
}
static void
thread_remove_pinned_certificate (GTask *task, gpointer object,
gpointer task_data, GCancellable *cancellable)
{
GckAttributes *attrs = task_data;
GError *error = NULL;
perform_remove_pinned_certificate (attrs, cancellable, &error);
if (error == NULL)
g_task_return_boolean (task, TRUE);
else
g_task_return_error (task, g_steal_pointer (&error));
}
/**
* gcr_trust_remove_pinned_certificate_async:
* @certificate: a #GcrCertificate
* @purpose: the purpose string
* @peer: the peer for this pinned certificate
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Remove a pinned certificate for communication with @peer for @purpose.
*
* If the same pinned certificate does not exist, or was already removed,
* then this operation succeeds without error.
*
* When the operation is finished, callback will be called. You can then call
* gcr_trust_remove_pinned_certificate_finish() to get the result of the
* operation.
*/
void
gcr_trust_remove_pinned_certificate_async (GcrCertificate *certificate,
const gchar *purpose,
const gchar *peer,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GckAttributes *attrs;
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
g_return_if_fail (purpose);
g_return_if_fail (peer);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_trust_remove_pinned_certificate_async);
attrs = prepare_remove_pinned_certificate (certificate, purpose, peer);
g_return_if_fail (attrs);
g_task_set_task_data (task, attrs, gck_attributes_unref);
g_task_run_in_thread (task, thread_remove_pinned_certificate);
g_clear_object (&task);
}
/**
* gcr_trust_remove_pinned_certificate_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_trust_remove_pinned_certificate_async().
*
* Returns: %TRUE if the pinned certificate no longer exists
*/
gboolean
gcr_trust_remove_pinned_certificate_finish (GAsyncResult *result, GError **error)
{
g_return_val_if_fail (!error || !*error, FALSE);
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* ----------------------------------------------------------------------------------
* CERTIFICATE ROOT
*/
static GckAttributes *
prepare_is_certificate_anchored (GcrCertificate *certificate, const gchar *purpose)
{
GckBuilder builder = GCK_BUILDER_INIT;
prepare_trust_attrs (certificate, CKT_X_ANCHORED_CERTIFICATE, &builder);
gck_builder_add_string (&builder, CKA_X_PURPOSE, purpose);
return gck_builder_end (&builder);
}
static gboolean
perform_is_certificate_anchored (GckAttributes *attrs,
GCancellable *cancellable,
GError **error)
{
GckEnumerator *en;
GList *slots;
GckObject *object;
if (!gcr_pkcs11_initialize (cancellable, error))
return FALSE;
slots = gcr_pkcs11_get_trust_lookup_slots ();
g_debug ("searching for certificate anchor in %d slots",
g_list_length (slots));
en = gck_slots_enumerate_objects (slots, attrs, 0);
g_clear_list (&slots, g_object_unref);
object = gck_enumerator_next (en, cancellable, error);
g_object_unref (en);
if (object != NULL)
g_object_unref (object);
g_debug ("%s certificate anchor", object ? "found" : "did not find");
return (object != NULL);
}
/**
* gcr_trust_is_certificate_anchored:
* @certificate: a #GcrCertificate to check
* @purpose: the purpose string
* @cancellable: a #GCancellable
* @error: a #GError, or %NULL
*
* Check if the @certificate is a trust anchor for the given @purpose. A trust
* anchor is used to verify the signatures on other certificates when verifying
* a certificate chain. Also known as a trusted certificate authority.
*
* This call may block, see [func@Gcr.trust_is_certificate_anchored_async] for
* the non-blocking version.
*
* In the case of an error, %FALSE is also returned. Check @error to detect
* if an error occurred.
*
* Returns: %TRUE if the certificate is a trust anchor
*/
gboolean
gcr_trust_is_certificate_anchored (GcrCertificate *certificate, const gchar *purpose,
GCancellable *cancellable, GError **error)
{
GckAttributes *search;
gboolean ret;
g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), FALSE);
g_return_val_if_fail (purpose, FALSE);
search = prepare_is_certificate_anchored (certificate, purpose);
g_return_val_if_fail (search, FALSE);
ret = perform_is_certificate_anchored (search, cancellable, error);
gck_attributes_unref (search);
return ret;
}
static void
thread_is_certificate_anchored (GTask *task, gpointer object,
gpointer task_data, GCancellable *cancellable)
{
GckAttributes *attrs = task_data;
GError *error = NULL;
gboolean found;
found = perform_is_certificate_anchored (attrs, cancellable, &error);
if (error == NULL)
g_task_return_boolean (task, found);
else
g_task_return_error (task, g_steal_pointer (&error));
}
/**
* gcr_trust_is_certificate_anchored_async:
* @certificate: a #GcrCertificate to check
* @purpose: the purpose string
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Check if the @certificate is a trust anchor for the given @purpose. A trust
* anchor is used to verify the signatures on other certificates when verifying
* a certificate chain. Also known as a trusted certificate authority.
*
* When the operation is finished, callback will be called. You can then call
* gcr_trust_is_certificate_anchored_finish() to get the result of the operation.
*/
void
gcr_trust_is_certificate_anchored_async (GcrCertificate *certificate, const gchar *purpose,
GCancellable *cancellable, GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GckAttributes *attrs;
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
g_return_if_fail (purpose);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_trust_is_certificate_anchored_async);
attrs = prepare_is_certificate_anchored (certificate, purpose);
g_return_if_fail (attrs);
g_task_set_task_data (task, attrs, gck_attributes_unref);
g_task_run_in_thread (task, thread_is_certificate_anchored);
g_clear_object (&task);
}
/**
* gcr_trust_is_certificate_anchored_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_trust_is_certificate_anchored_async().
*
* In the case of an error, %FALSE is also returned. Check @error to detect
* if an error occurred.
*
* Returns: %TRUE if the certificate is a trust anchor
*/
gboolean
gcr_trust_is_certificate_anchored_finish (GAsyncResult *result, GError **error)
{
g_return_val_if_fail (!error || !*error, FALSE);
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* ----------------------------------------------------------------------------------
* DISTRUSTED CERTIFICATE
*/
static GckAttributes *
prepare_is_certificate_distrusted (unsigned char *serial_nr,
size_t serial_nr_len,
unsigned char *issuer,
size_t issuer_len)
{
GckBuilder builder = GCK_BUILDER_INIT;
gck_builder_add_ulong (&builder, CKA_CLASS, CKO_X_TRUST_ASSERTION);
gck_builder_add_ulong (&builder, CKA_X_ASSERTION_TYPE, CKT_X_DISTRUSTED_CERTIFICATE);
gck_builder_add_data (&builder, CKA_SERIAL_NUMBER, serial_nr, serial_nr_len);
gck_builder_add_data (&builder, CKA_ISSUER, issuer, issuer_len);
return gck_builder_end (&builder);
}
static gboolean
perform_is_certificate_distrusted (GckAttributes *attrs,
GCancellable *cancellable,
GError **error)
{
GckEnumerator *en;
GList *slots;
GckObject *object;
if (!gcr_pkcs11_initialize (cancellable, error))
return FALSE;
slots = gcr_pkcs11_get_trust_lookup_slots ();
g_debug ("searching for certificate distrust assertion in %d slots",
g_list_length (slots));
en = gck_slots_enumerate_objects (slots, attrs, 0);
g_clear_list (&slots, g_object_unref);
object = gck_enumerator_next (en, cancellable, error);
g_object_unref (en);
if (object != NULL)
g_object_unref (object);
g_debug ("%s certificate distrust", object ? "found" : "did not find");
return (object != NULL);
}
/**
* gcr_trust_is_certificate_distrusted:
* @serial_nr: (array length=serial_nr_len): The serial number of the certificate
* @serial_nr_len: The nr of bytes in @serial_nr
* @issuer: (array length=issuer_len): The raw issuer
* @issuer_len: The nr of bytes in @issuer
* @cancellable: (nullable): a #GCancellable or %NULL
* @error: a #GError, or %NULL
*
* Checks whether the certificate that can be uniquely identified with the
* given @serial_nr and @issuer is marked as distrusted (for example by the
* user, or because it's part of a CRL).
*
* Since we can't directly use [iface@Certificate] to fetch these values, you
* need to call these with the raw serial number and issuer as provided by the
* PKCS#11 fields `CKA_SERIAL_NR` and `CKA_ISSUER`.
*
* Returns: %TRUE if the certificate is marked as distrusted
*/
gboolean
gcr_trust_is_certificate_distrusted (unsigned char *serial_nr,
size_t serial_nr_len,
unsigned char *issuer,
size_t issuer_len,
GCancellable *cancellable,
GError **error)
{
GckAttributes *search;
gboolean ret;
g_return_val_if_fail (serial_nr, FALSE);
g_return_val_if_fail (serial_nr_len > 0, FALSE);
g_return_val_if_fail (issuer, FALSE);
g_return_val_if_fail (issuer_len > 0, FALSE);
g_return_val_if_fail (G_IS_CANCELLABLE (cancellable) || !cancellable, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
search = prepare_is_certificate_distrusted (serial_nr, serial_nr_len, issuer, issuer_len);
g_return_val_if_fail (search, FALSE);
ret = perform_is_certificate_distrusted (search, cancellable, error);
gck_attributes_unref (search);
return ret;
}
static void
thread_is_certificate_distrusted (GTask *task,
void *object,
void *task_data,
GCancellable *cancellable)
{
GckAttributes *attrs = task_data;
GError *error = NULL;
gboolean found;
found = perform_is_certificate_distrusted (attrs, cancellable, &error);
if (error == NULL)
g_task_return_boolean (task, found);
else
g_task_return_error (task, g_steal_pointer (&error));
}
/**
* gcr_trust_is_certificate_distrusted_async:
* @serial_nr: (array length=serial_nr_len): The serial number of the certificate
* @serial_nr_len: The nr of bytes in @serial_nr
* @issuer: (array length=issuer_len): The raw issuer
* @issuer_len: The number of bytes in @issuer
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Asynchronously checks whether the certificate that can be uniquely
* identified with the given @serial_nr and @issuer is marked as distrusted
* (for example by the user, or because it's part of a CRL).
*
* Since we can't directly use [iface@Certificate] to fetch these values, you
* need to call these with the raw serial number and issuer as provided by the
* PKCS#11 fields `CKA_SERIAL_NR` and `CKA_ISSUER`.
*
* When the operation is finished, @callback will be called. You can then call
* [func@trust_is_certificate_distrusted_finish] to get the result of the
* operation.
*/
void
gcr_trust_is_certificate_distrusted_async (unsigned char *serial_nr,
size_t serial_nr_len,
unsigned char *issuer,
size_t issuer_len,
GCancellable *cancellable,
GAsyncReadyCallback callback,
void *user_data)
{
GTask *task;
GckAttributes *attrs;
g_return_if_fail (serial_nr);
g_return_if_fail (serial_nr_len > 0);
g_return_if_fail (issuer);
g_return_if_fail (issuer_len > 0);
g_return_if_fail (G_IS_CANCELLABLE (cancellable) || !cancellable);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_trust_is_certificate_distrusted_async);
attrs = prepare_is_certificate_distrusted (serial_nr, serial_nr_len, issuer, issuer_len);
g_return_if_fail (attrs);
g_task_set_task_data (task, attrs, gck_attributes_unref);
g_task_run_in_thread (task, thread_is_certificate_distrusted);
g_clear_object (&task);
}
/**
* gcr_trust_is_certificate_distrusted_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* [func@trust_is_certificate_distrusted_async].
*
* In the case of an error, %FALSE is also returned. Check @error to detect
* if an error occurred.
*
* Returns: %TRUE if the certificate is a trust anchor
*/
gboolean
gcr_trust_is_certificate_distrusted_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}