/*
* 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-pkcs11-certificate.h"
#include "gcr-certificate.h"
#include "gcr-internal.h"
#include "gcr-library.h"
#include
#include
/**
* GcrPkcs11Certificate:
*
* A certificate loaded from a PKCS#11 storage.
* It is also a valid [class@Gck.Object] and can be used as such.
*
* Use gcr_pkcs11_certificate_lookup_issuer() to lookup the issuer of a given
* certificate in the PKCS#11 store.
*
* Various common PKCS#11 certificate attributes are automatically loaded and
* are available via gcr_pkcs11_certificate_get_attributes().
*/
enum {
PROP_0,
PROP_ATTRIBUTES
};
struct _GcrPkcs11CertificatePrivate {
GckAttributes *attrs;
};
static void gcr_certificate_iface (GcrCertificateIface *iface);
G_DEFINE_TYPE_WITH_CODE (GcrPkcs11Certificate, gcr_pkcs11_certificate, GCK_TYPE_OBJECT,
G_ADD_PRIVATE (GcrPkcs11Certificate);
G_IMPLEMENT_INTERFACE (GCR_TYPE_CERTIFICATE, gcr_certificate_iface);
);
static GckAttributes *
prepare_lookup_certificate_issuer (GcrCertificate *cert)
{
GckBuilder builder = GCK_BUILDER_INIT;
gpointer data;
gsize n_data;
gck_builder_add_ulong (&builder, CKA_CLASS, CKO_CERTIFICATE);
gck_builder_add_ulong (&builder, CKA_CERTIFICATE_TYPE, CKC_X_509);
data = gcr_certificate_get_issuer_raw (cert, &n_data);
gck_builder_add_data (&builder, CKA_SUBJECT, data, n_data);
g_free (data);
return gck_builder_end (&builder);
}
static GcrCertificate*
perform_lookup_certificate (GckAttributes *search,
GCancellable *cancellable,
GError **error)
{
GcrCertificate *cert;
GckObject *object;
GckAttributes *attrs;
GckModule *module;
GckSession *session;
GckEnumerator *en;
GList *modules;
if (!gcr_pkcs11_initialize (cancellable, error))
return NULL;
modules = gcr_pkcs11_get_modules ();
en = gck_modules_enumerate_objects (modules, search, 0);
g_clear_list (&modules, g_object_unref);
object = gck_enumerator_next (en, cancellable, error);
g_object_unref (en);
if (object == NULL)
return NULL;
/*
* Only the CKA_VALUE, CKA_CLASS and CKA_CERTIFICATE_TYPE
* is strictly necessary here, but we get more attrs.
*/
attrs = gck_object_get (object, cancellable, error,
CKA_VALUE, CKA_LABEL,
CKA_ID, CKA_CLASS,
CKA_CERTIFICATE_TYPE,
CKA_ISSUER,
CKA_SERIAL_NUMBER,
GCK_INVALID);
if (attrs == NULL) {
g_object_unref (object);
return NULL;
}
module = gck_object_get_module (object);
session = gck_object_get_session (object);
cert = g_object_new (GCR_TYPE_PKCS11_CERTIFICATE,
"module", module,
"handle", gck_object_get_handle (object),
"session", session,
"attributes", attrs,
NULL);
g_object_unref (module);
g_object_unref (session);
g_object_unref (object);
gck_attributes_unref (attrs);
return cert;
}
static void
thread_lookup_certificate (GTask *task, gpointer src_object, gpointer task_data,
GCancellable *cancellable)
{
GckAttributes *search = (GckAttributes *) task_data;
GcrCertificate *result;
GError *error = NULL;
result = perform_lookup_certificate (search, cancellable, &error);
if (error != NULL) {
g_task_return_error (task, g_steal_pointer (&error));
g_clear_error (&error);
} else{
g_task_return_pointer (task, result, g_object_unref);
}
}
/* ----------------------------------------------------------------------------
* OBJECT
*/
static GObject*
gcr_pkcs11_certificate_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
gpointer obj = G_OBJECT_CLASS (gcr_pkcs11_certificate_parent_class)->constructor (type, n_props, props);
GckAttributes *attrs;
const GckAttribute *attr;
gulong value;
attrs = gcr_pkcs11_certificate_get_attributes (obj);
g_return_val_if_fail (attrs, NULL);
if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &value) ||
value != CKO_CERTIFICATE) {
g_warning ("attributes don't contain a certificate with: %s",
"CKA_CLASS == CKO_CERTIFICATE");
return NULL;
}
if (!gck_attributes_find_ulong (attrs, CKA_CERTIFICATE_TYPE, &value) ||
value != CKC_X_509) {
g_warning ("attributes don't contain a certificate with: %s",
"CKA_CERTIFICATE_TYPE == CKC_X_509");
return NULL;
}
attr = gck_attributes_find (attrs, CKA_VALUE);
if (!attr || !attr->value || attr->length == 0 || attr->length == G_MAXULONG) {
g_warning ("attributes don't contain a valid: CKA_VALUE");
return NULL;
}
return obj;
}
static void
gcr_pkcs11_certificate_init (GcrPkcs11Certificate *self)
{
self->pv = gcr_pkcs11_certificate_get_instance_private (self);
}
static void
gcr_pkcs11_certificate_set_property (GObject *obj, guint prop_id, const GValue *value,
GParamSpec *pspec)
{
GcrPkcs11Certificate *self = GCR_PKCS11_CERTIFICATE (obj);
switch (prop_id) {
case PROP_ATTRIBUTES:
g_return_if_fail (self->pv->attrs == NULL);
self->pv->attrs = g_value_dup_boxed (value);
g_return_if_fail (self->pv->attrs != NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gcr_pkcs11_certificate_get_property (GObject *obj, guint prop_id, GValue *value,
GParamSpec *pspec)
{
GcrPkcs11Certificate *self = GCR_PKCS11_CERTIFICATE (obj);
switch (prop_id) {
case PROP_ATTRIBUTES:
g_value_set_boxed (value, gcr_pkcs11_certificate_get_attributes (self));
break;
default:
gcr_certificate_mixin_get_property (obj, prop_id, value, pspec);
break;
}
}
static void
gcr_pkcs11_certificate_finalize (GObject *obj)
{
GcrPkcs11Certificate *self = GCR_PKCS11_CERTIFICATE (obj);
gck_attributes_unref (self->pv->attrs);
G_OBJECT_CLASS (gcr_pkcs11_certificate_parent_class)->finalize (obj);
}
static void
gcr_pkcs11_certificate_class_init (GcrPkcs11CertificateClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructor = gcr_pkcs11_certificate_constructor;
gobject_class->get_property = gcr_pkcs11_certificate_get_property;
gobject_class->set_property = gcr_pkcs11_certificate_set_property;
gobject_class->finalize = gcr_pkcs11_certificate_finalize;
/**
* GcrPkcs11Certificate:attributes:
*
* Automatically loaded attributes for this certificate.
*/
g_object_class_install_property (gobject_class, PROP_ATTRIBUTES,
g_param_spec_boxed ("attributes", "Attributes", "The data displayed in the renderer",
GCK_TYPE_ATTRIBUTES,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
gcr_certificate_mixin_class_init (gobject_class);
_gcr_initialize_library ();
}
static const guchar *
gcr_pkcs11_certificate_get_der_data (GcrCertificate *cert,
gsize *n_data)
{
GcrPkcs11Certificate *self = GCR_PKCS11_CERTIFICATE (cert);
const GckAttribute *attr;
g_return_val_if_fail (GCR_IS_CERTIFICATE (self), NULL);
g_return_val_if_fail (n_data, NULL);
g_return_val_if_fail (self->pv->attrs, NULL);
attr = gck_attributes_find (self->pv->attrs, CKA_VALUE);
g_return_val_if_fail (attr && attr->length != 0 && attr->length != G_MAXULONG, NULL);
*n_data = attr->length;
return attr->value;
}
static void
gcr_certificate_iface (GcrCertificateIface *iface)
{
iface->get_der_data = gcr_pkcs11_certificate_get_der_data;
}
/* -----------------------------------------------------------------------------
* PUBLIC
*/
/**
* gcr_pkcs11_certificate_get_attributes:
* @self: A #GcrPkcs11Certificate
*
* Access the automatically loaded attributes for this certificate.
*
* Returns: (transfer none): the certificate attributes
*/
GckAttributes *
gcr_pkcs11_certificate_get_attributes (GcrPkcs11Certificate *self)
{
g_return_val_if_fail (GCR_IS_PKCS11_CERTIFICATE (self), NULL);
return self->pv->attrs;
}
/**
* gcr_pkcs11_certificate_lookup_issuer:
* @certificate: a #GcrCertificate
* @cancellable: a #GCancellable
* @error: a #GError, or %NULL
*
* Lookup a the issuer of a @certificate in the PKCS#11 storage. The
* lookup is done using the issuer DN of the certificate. No certificate chain
* verification is done. Use a crypto library to make trust decisions.
*
* This call may block, see gcr_pkcs11_certificate_lookup_issuer() for the
* non-blocking version.
*
* Will return %NULL if no issuer certificate is found. Use @error to determine
* if an error occurred.
*
* Returns: (transfer full): a new #GcrPkcs11Certificate, or %NULL
*/
GcrCertificate *
gcr_pkcs11_certificate_lookup_issuer (GcrCertificate *certificate, GCancellable *cancellable,
GError **error)
{
GckAttributes *search;
GcrCertificate *issuer;
g_return_val_if_fail (GCR_IS_CERTIFICATE (certificate), NULL);
if (!gcr_pkcs11_initialize (cancellable, error))
return NULL;
search = prepare_lookup_certificate_issuer (certificate);
g_return_val_if_fail (search, FALSE);
issuer = perform_lookup_certificate (search, cancellable, error);
gck_attributes_unref (search);
return issuer;
}
/**
* gcr_pkcs11_certificate_lookup_issuer_async:
* @certificate: a #GcrCertificate
* @cancellable: a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Lookup a the issuer of a @certificate in the PKCS#11 storage. The
* lookup is done using the issuer DN of the certificate. No certificate chain
* verification is done. Use a crypto library to make trust decisions.
*
* When the operation is finished, callback will be called. You can then call
* gcr_pkcs11_certificate_lookup_issuer_finish() to get the result of the
* operation.
*/
void
gcr_pkcs11_certificate_lookup_issuer_async (GcrCertificate *certificate, GCancellable *cancellable,
GAsyncReadyCallback callback, gpointer user_data)
{
GTask *task;
GckAttributes *search;
g_return_if_fail (GCR_IS_CERTIFICATE (certificate));
task = g_task_new (certificate, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_pkcs11_certificate_lookup_issuer_async);
search = prepare_lookup_certificate_issuer (certificate);
g_return_if_fail (search);
g_task_set_task_data (task, search, gck_attributes_unref);
g_task_run_in_thread (task, thread_lookup_certificate);
g_object_unref (task);
}
/**
* gcr_pkcs11_certificate_lookup_issuer_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_pkcs11_certificate_lookup_issuer_async().
*
* Will return %NULL if no issuer certificate is found. Use @error to determine
* if an error occurred.
*
* Returns: (transfer full): a new #GcrPkcs11Certificate, or %NULL
*/
GcrCertificate *
gcr_pkcs11_certificate_lookup_issuer_finish (GAsyncResult *result, GError **error)
{
GObject *source;
g_return_val_if_fail (G_IS_TASK (result), NULL);
source = g_task_get_source_object (G_TASK (result));
g_return_val_if_fail (g_task_is_valid (result, source), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* gcr_pkcs11_certificate_new_from_uri:
* @pkcs11_uri: a A PKCS \#11 URI
* @cancellable: (nullable): a #GCancellable
* @error: a #GError, or %NULL
*
* Lookup a certificate in the PKCS#11 storage by the given URI.
*
* This call may block, see gcr_pkcs11_certificate_new_from_uri_async() for the
* non-blocking version.
*
* Will return %NULL if no certificate is found. Use @error to determine
* if an error occurred.
*
* Returns: (transfer full) (nullable): a new #GcrCertificate, or %NULL
*/
GcrCertificate *
gcr_pkcs11_certificate_new_from_uri (const gchar *pkcs11_uri,
GCancellable *cancellable,
GError **error)
{
GckUriData *data;
GcrCertificate *cert;
g_return_val_if_fail (pkcs11_uri != NULL, NULL);
g_return_val_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
data = gck_uri_data_parse (pkcs11_uri, GCK_URI_FOR_OBJECT, error);
g_return_val_if_fail (data != NULL, NULL);
cert = perform_lookup_certificate (data->attributes, cancellable, error);
gck_uri_data_free (data);
return cert;
}
/**
* gcr_pkcs11_certificate_new_from_uri_async:
* @pkcs11_uri: a A PKCS \#11 URI
* @cancellable: (nullable): a #GCancellable
* @callback: a #GAsyncReadyCallback to call when the operation completes
* @user_data: the data to pass to callback function
*
* Lookup a certificate in the PKCS#11 storage by the given URI.
*
* When the operation is finished, callback will be called. You can then call
* gcr_pkcs11_certificate_new_from_uri_finish() to get the result of the
* operation.
*/
void
gcr_pkcs11_certificate_new_from_uri_async (const gchar *pkcs11_uri,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GckUriData *data;
GError *error = NULL;
g_return_if_fail (pkcs11_uri != NULL);
g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, gcr_pkcs11_certificate_new_from_uri_async);
data = gck_uri_data_parse (pkcs11_uri, GCK_URI_FOR_OBJECT, &error);
if (data == NULL) {
g_task_return_error (task, error);
return;
}
g_task_set_task_data (task, g_steal_pointer (&data->attributes), gck_attributes_unref);
gck_uri_data_free (data);
g_task_run_in_thread (task, thread_lookup_certificate);
g_object_unref (task);
}
/**
* gcr_pkcs11_certificate_new_from_uri_finish:
* @result: the #GAsyncResult passed to the callback
* @error: a #GError, or %NULL
*
* Finishes an asynchronous operation started by
* gcr_pkcs11_certificate_new_from_uri_async().
*
* Will return %NULL if no certificate is found. Use @error to determine
* if an error occurred.
*
* Returns: (transfer full) (nullable): a new #GcrCertificate, or %NULL
*/
GcrCertificate *
gcr_pkcs11_certificate_new_from_uri_finish (GAsyncResult *result,
GError **error)
{
GObject *source;
g_return_val_if_fail (G_IS_TASK (result), NULL);
source = g_task_get_source_object (G_TASK (result));
g_return_val_if_fail (g_task_is_valid (result, source), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}