diff options
-rw-r--r-- | gcr/Makefile.am | 2 | ||||
-rw-r--r-- | gcr/gcr-base.h | 1 | ||||
-rw-r--r-- | gcr/gcr-certificate-request.c | 493 | ||||
-rw-r--r-- | gcr/gcr-certificate-request.h | 71 | ||||
-rw-r--r-- | gcr/gcr-oids.list | 9 | ||||
-rw-r--r-- | gcr/tests/Makefile.am | 5 | ||||
-rw-r--r-- | gcr/tests/console-interaction.c | 175 | ||||
-rw-r--r-- | gcr/tests/console-interaction.h | 34 | ||||
-rw-r--r-- | gcr/tests/frob-certificate-request.c | 114 |
9 files changed, 901 insertions, 3 deletions
diff --git a/gcr/Makefile.am b/gcr/Makefile.am index addfca0..f8ea461 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -17,6 +17,7 @@ HEADER_BASE_FILES = \ gcr-base.h \ gcr-certificate.h \ gcr-certificate-chain.h \ + gcr-certificate-request.h \ gcr-collection.h \ gcr-comparable.h \ gcr-deprecated-base.h \ @@ -98,6 +99,7 @@ libgcr_base_@GCR_MAJOR@_la_SOURCES = \ gcr-callback-output-stream.c gcr-callback-output-stream.h \ gcr-certificate.c gcr-certificate.h \ gcr-certificate-chain.c gcr-certificate-chain.h \ + gcr-certificate-request.c gcr-certificate-request.h \ gcr-collection.c gcr-collection.h \ gcr-comparable.c gcr-comparable.h \ gcr-debug.c gcr-debug.h \ diff --git a/gcr/gcr-base.h b/gcr/gcr-base.h index 753c381..ff871cb 100644 --- a/gcr/gcr-base.h +++ b/gcr/gcr-base.h @@ -36,6 +36,7 @@ #include "gcr-certificate.h" #include "gcr-certificate-chain.h" +#include "gcr-certificate-request.h" #include "gcr-deprecated-base.h" #include "gcr-enum-types-base.h" #include "gcr-filter-collection.h" diff --git a/gcr/gcr-certificate-request.c b/gcr/gcr-certificate-request.c new file mode 100644 index 0000000..a097e5b --- /dev/null +++ b/gcr/gcr-certificate-request.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include "gcr-certificate-request.h" +#include "gcr-enum-types-base.h" +#include "gcr-oids.h" +#include "gcr-subject-public-key.h" + +#include <egg/egg-asn1x.h> +#include <egg/egg-asn1-defs.h> +#include <egg/egg-dn.h> + +#include <glib/gi18n-lib.h> + +#define GCR_CERTIFICATE_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_CERTIFICATE_REQUEST, GcrCertificateRequestClass)) +#define GCR_IS_CERTIFICATE_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_CERTIFICATE_REQUEST)) +#define GCR_CERTIFICATE_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_CERTIFICATE_REQUEST, GcrCertificateRequestClass)) + +typedef struct _GcrCertificateRequestClass GcrCertificateRequestClass; + +struct _GcrCertificateRequest { + GObject parent; + + GckObject *private_key; + GNode *asn; +}; + +struct _GcrCertificateRequestClass { + GObjectClass parent_class; +}; + +enum { + PROP_0, + PROP_FORMAT, + PROP_PRIVATE_KEY +}; + +/* Forward declarations */ +G_DEFINE_TYPE (GcrCertificateRequest, gcr_certificate_request, G_TYPE_OBJECT); + +static void +gcr_certificate_request_init (GcrCertificateRequest *self) +{ + +} + +static void +gcr_certificate_request_constructed (GObject *obj) +{ + GcrCertificateRequest *self = GCR_CERTIFICATE_REQUEST (obj); + GNode *version; + + G_OBJECT_CLASS (gcr_certificate_request_parent_class)->constructed (obj); + + self->asn = egg_asn1x_create (pkix_asn1_tab, "pkcs-10-CertificationRequest"); + g_return_if_fail (self->asn != NULL); + + /* Setup the version */ + version = egg_asn1x_node (self->asn, "certificationRequestInfo", "version", NULL); + egg_asn1x_set_integer_as_ulong (version, 0); +} + +static void +gcr_certificate_request_finalize (GObject *obj) +{ + GcrCertificateRequest *self = GCR_CERTIFICATE_REQUEST (obj); + + egg_asn1x_destroy (self->asn); + + G_OBJECT_CLASS (gcr_certificate_request_parent_class)->finalize (obj); +} + +static void +gcr_certificate_request_set_property (GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcrCertificateRequest *self = GCR_CERTIFICATE_REQUEST (obj); + GcrCertificateRequestFormat format; + + switch (prop_id) { + case PROP_PRIVATE_KEY: + g_return_if_fail (self->private_key == NULL); + self->private_key = g_value_dup_object (value); + g_return_if_fail (GCK_IS_OBJECT (self->private_key)); + break; + case PROP_FORMAT: + format = g_value_get_enum (value); + g_return_if_fail (format == GCR_CERTIFICATE_REQUEST_PKCS10); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_request_get_property (GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcrCertificateRequest *self = GCR_CERTIFICATE_REQUEST (obj); + + switch (prop_id) { + case PROP_PRIVATE_KEY: + g_value_set_object (value, self->private_key); + break; + case PROP_FORMAT: + g_value_set_enum (value, GCR_CERTIFICATE_REQUEST_PKCS10); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_certificate_request_class_init (GcrCertificateRequestClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + _gcr_oids_init (); + + gobject_class->constructed = gcr_certificate_request_constructed; + gobject_class->finalize = gcr_certificate_request_finalize; + gobject_class->set_property = gcr_certificate_request_set_property; + gobject_class->get_property = gcr_certificate_request_get_property; + + g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY, + g_param_spec_object ("private-key", "Private key", "Private key for request", + GCK_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (gobject_class, PROP_FORMAT, + g_param_spec_enum ("format", "Format", "Format of certificate request", + GCR_TYPE_CERTIFICATE_REQUEST_FORMAT, GCR_CERTIFICATE_REQUEST_PKCS10, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + +/** + * gcr_certificate_request_prepare: + * @format: the format for the certificate request + * @private_key: the private key the the certificate is being requested for + * + * xxx + * + * Returns: (transfer full): a new #GcrCertificate request + */ +GcrCertificateRequest * +gcr_certificate_request_prepare (GcrCertificateRequestFormat format, + GckObject *private_key) +{ + g_return_val_if_fail (format == GCR_CERTIFICATE_REQUEST_PKCS10, NULL); + g_return_val_if_fail (GCK_IS_OBJECT (private_key), NULL); + + return g_object_new (GCR_TYPE_CERTIFICATE_REQUEST, + "format", format, + "private-key", private_key, + NULL); +} + +void +gcr_certificate_request_set_cn (GcrCertificateRequest *self, + const gchar *cn) +{ + GNode *subject; + GNode *dn; + + g_return_if_fail (GCR_IS_CERTIFICATE_REQUEST (self)); + g_return_if_fail (cn != NULL); + + subject = egg_asn1x_node (self->asn, "certificationRequestInfo", "subject", NULL); + dn = egg_asn1x_node (subject, "rdnSequence", NULL); + + /* TODO: we shouldn't really be clearing this, but replacing CN */ + egg_asn1x_set_choice (subject, dn); + egg_asn1x_clear (dn); + egg_dn_add_string_part (dn, GCR_OID_NAME_CN, cn); +} + + +static EggBytes * +prepare_to_be_signed (GcrCertificateRequest *self) +{ + GNode *node; + + node = egg_asn1x_node (self->asn, "certificationRequestInfo", NULL); + return egg_asn1x_encode (node, NULL); +} + +static gboolean +prepare_subject_public_key_and_mechanism (GcrCertificateRequest *self, + GNode *subject_public_key, + GQuark *algorithm, + GckMechanism *mechanism, + GError **error) +{ + EggBytes *encoded; + GNode *node; + GQuark oid; + + g_assert (algorithm != NULL); + g_assert (mechanism != NULL); + + encoded = egg_asn1x_encode (subject_public_key, NULL); + g_return_val_if_fail (encoded != NULL, FALSE); + + node = egg_asn1x_node (subject_public_key, "algorithm", "algorithm", NULL); + oid = egg_asn1x_get_oid_as_quark (node); + + memset (mechanism, 0, sizeof (GckMechanism)); + if (oid == GCR_OID_PKIX1_RSA) { + mechanism->type = CKM_SHA1_RSA_PKCS; + *algorithm = GCR_OID_PKIX1_SHA1_WITH_RSA; + + } else if (oid == GCR_OID_PKIX1_DSA) { + mechanism->type = CKM_DSA_SHA1; + *algorithm = GCR_OID_PKIX1_SHA1_WITH_DSA; + + } else { + egg_bytes_unref (encoded); + g_set_error (error, GCR_DATA_ERROR, GCR_ERROR_UNRECOGNIZED, + _("Unsupported key type for certificate request")); + return FALSE; + } + + node = egg_asn1x_node (self->asn, "certificationRequestInfo", "subjectPKInfo", NULL); + if (!egg_asn1x_set_element_raw (node, encoded)) + g_return_val_if_reached (FALSE); + + egg_bytes_unref (encoded); + return TRUE; +} + +static void +encode_take_signature_into_request (GcrCertificateRequest *self, + GQuark algorithm, + GNode *subject_public_key, + guchar *result, + gsize n_result) +{ + EggBytes *data; + GNode *params; + GNode *node; + + node = egg_asn1x_node (self->asn, "signature", NULL); + egg_asn1x_take_bits_as_raw (node, egg_bytes_new_take (result, n_result), n_result * 8); + + node = egg_asn1x_node (self->asn, "signatureAlgorithm", "algorithm", NULL); + egg_asn1x_set_oid_as_quark (node, algorithm); + + node = egg_asn1x_node (self->asn, "signatureAlgorithm", "parameters", NULL); + params = egg_asn1x_node (subject_public_key, "algorithm", "parameters", NULL); + data = egg_asn1x_encode (params, NULL); + egg_asn1x_set_element_raw (node, data); + egg_bytes_unref (data); +} + +gboolean +gcr_certificate_request_complete (GcrCertificateRequest *self, + GCancellable *cancellable, + GError **error) +{ + GNode *subject_public_key; + GckMechanism mechanism = { 0, }; + GQuark algorithm = 0; + EggBytes *tbs; + GckSession *session; + guchar *signature; + gsize n_signature; + gboolean ret; + + g_return_val_if_fail (GCR_IS_CERTIFICATE_REQUEST (self), FALSE); + g_return_val_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + subject_public_key = _gcr_subject_public_key_load (self->private_key, + cancellable, error); + if (subject_public_key == NULL) + return FALSE; + + ret = prepare_subject_public_key_and_mechanism (self, subject_public_key, + &algorithm, &mechanism, error); + + if (!ret) { + egg_asn1x_destroy (subject_public_key); + return FALSE; + } + + tbs = prepare_to_be_signed (self); + session = gck_object_get_session (self->private_key); + signature = gck_session_sign_full (session, self->private_key, &mechanism, + egg_bytes_get_data (tbs), + egg_bytes_get_size (tbs), + &n_signature, cancellable, error); + g_object_unref (session); + egg_bytes_unref (tbs); + + if (!signature) { + egg_asn1x_destroy (subject_public_key); + return FALSE; + } + + encode_take_signature_into_request (self, algorithm, subject_public_key, + signature, n_signature); + egg_asn1x_destroy (subject_public_key); + return TRUE; +} + +typedef struct { + GcrCertificateRequest *request; + GCancellable *cancellable; + GQuark algorithm; + GNode *subject_public_key; + GckMechanism mechanism; + GckSession *session; + EggBytes *tbs; +} CompleteClosure; + +static void +complete_closure_free (gpointer data) +{ + CompleteClosure *closure = data; + egg_asn1x_destroy (closure->subject_public_key); + g_clear_object (&closure->request); + g_clear_object (&closure->cancellable); + g_clear_object (&closure->session); + if (closure->tbs) + egg_bytes_unref (closure->tbs); + g_free (closure); +} + +static void +on_certificate_request_signed (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + CompleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + guchar *signature; + gsize n_signature; + + signature = gck_session_sign_finish (closure->session, result, &n_signature, &error); + if (result == NULL) { + encode_take_signature_into_request (closure->request, + closure->algorithm, + closure->subject_public_key, + signature, n_signature); + + } else { + g_simple_async_result_take_error (res, error); + } + + g_simple_async_result_complete (res); + g_object_unref (res); +} + +static void +on_subject_public_key_loaded (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data); + CompleteClosure *closure = g_simple_async_result_get_op_res_gpointer (res); + GError *error = NULL; + + closure->subject_public_key = _gcr_subject_public_key_load_finish (result, &error); + if (error == NULL) { + prepare_subject_public_key_and_mechanism (closure->request, + closure->subject_public_key, + &closure->algorithm, + &closure->mechanism, + &error); + } + + if (error != NULL) { + g_simple_async_result_take_error (res, error); + g_simple_async_result_complete (res); + + } else { + closure->tbs = prepare_to_be_signed (closure->request); + gck_session_sign_async (closure->session, + closure->request->private_key, + &closure->mechanism, + egg_bytes_get_data (closure->tbs), + egg_bytes_get_size (closure->tbs), + closure->cancellable, + on_certificate_request_signed, + g_object_ref (res)); + } + + g_object_unref (res); +} + +void +gcr_certificate_request_complete_async (GcrCertificateRequest *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + CompleteClosure *closure; + + g_return_if_fail (GCR_IS_CERTIFICATE_REQUEST (self)); + g_return_if_fail (cancellable == NULL || G_CANCELLABLE (cancellable)); + + res = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + gcr_certificate_request_complete_async); + closure = g_new0 (CompleteClosure, 1); + closure->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + closure->session = gck_object_get_session (self->private_key); + closure->request = g_object_ref (self); + g_simple_async_result_set_op_res_gpointer (res, closure, complete_closure_free); + + _gcr_subject_public_key_load_async (self->private_key, cancellable, + on_subject_public_key_loaded, + g_object_ref (res)); + + g_object_unref (res); +} + +/** + * + */ +gboolean +gcr_certificate_request_complete_finish (GcrCertificateRequest *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (GCR_IS_CERTIFICATE_REQUEST (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), + gcr_certificate_request_complete_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + + return TRUE; +} + +/** + * gcr_certificate_request_encode: + * @self: a certificate request + * @length: location to place length of returned data + * + * Encode the certificate request. It must have been completed with + * gcr_certificate_request_complete() or gcr_certificate_request_complete_async() + * + * The output is a DER encoded certificate request. + * + * Returns: (transfer full) (array length=length): the encoded certificate request + */ +guchar * +gcr_certificate_request_encode (GcrCertificateRequest *self, + gsize *length) +{ + EggBytes *bytes; + + g_return_val_if_fail (GCR_IS_CERTIFICATE_REQUEST (self), NULL); + g_return_val_if_fail (length != NULL, NULL); + + bytes = egg_asn1x_encode (self->asn, NULL); + if (bytes == NULL) { + g_warning ("couldn't encode certificate request: %s", + egg_asn1x_message (self->asn)); + return NULL; + } + + *length = egg_bytes_get_size (bytes); + return g_byte_array_free (egg_bytes_unref_to_array (bytes), FALSE); +} diff --git a/gcr/gcr-certificate-request.h b/gcr/gcr-certificate-request.h new file mode 100644 index 0000000..b83ba0f --- /dev/null +++ b/gcr/gcr-certificate-request.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#if !defined (__GCR_INSIDE_HEADER__) && !defined (GCR_COMPILATION) +#error "Only <gcr/gcr.h> or <gcr/gcr-base.h> can be included directly." +#endif + +#ifndef __GCR_CERTIFICATE_REQUEST_H__ +#define __GCR_CERTIFICATE_REQUEST_H__ + +#include <glib-object.h> + +#include "gcr-types.h" + +G_BEGIN_DECLS + +typedef enum { + GCR_CERTIFICATE_REQUEST_PKCS10 = 1, +} GcrCertificateRequestFormat; + +#define GCR_TYPE_CERTIFICATE_REQUEST (gcr_certificate_request_get_type ()) +#define GCR_CERTIFICATE_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_CERTIFICATE_REQUEST, GcrCertificateRequest)) +#define GCR_IS_CERTIFICATE_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_CERTIFICATE_REQUEST)) + +typedef struct _GcrCertificateRequest GcrCertificateRequest; + +GType gcr_certificate_request_get_type (void) G_GNUC_CONST; + +GcrCertificateRequest * gcr_certificate_request_prepare (GcrCertificateRequestFormat format, + GckObject *private_key); + +void gcr_certificate_request_set_cn (GcrCertificateRequest *self, + const gchar *cn); + +gboolean gcr_certificate_request_complete (GcrCertificateRequest *self, + GCancellable *cancellable, + GError **error); + +void gcr_certificate_request_complete_async (GcrCertificateRequest *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gboolean gcr_certificate_request_complete_finish (GcrCertificateRequest *self, + GAsyncResult *result, + GError **error); + +guchar * gcr_certificate_request_get_der_data (GcrCertificateRequest *request, + gsize *length); + +G_END_DECLS + +#endif /* __GCR_CERTIFICATE_REQUEST_H__ */ diff --git a/gcr/gcr-oids.list b/gcr/gcr-oids.list index 2fe3364..b49f4bd 100644 --- a/gcr/gcr-oids.list +++ b/gcr/gcr-oids.list @@ -1,11 +1,15 @@ +NAME_CN 2.5.4.3 + BASIC_CONSTRAINTS 2.5.29.19 EXTENDED_KEY_USAGE 2.5.29.37 SUBJECT_KEY_IDENTIFIER 2.5.29.14 KEY_USAGE 2.5.29.15 SUBJECT_ALT_NAME 2.5.29.17 -PKIX1_RSA 1.2.840.113549.1.1.1 -PKIX1_DSA 1.2.840.10040.4.1 +PKIX1_RSA 1.2.840.113549.1.1.1 +PKIX1_SHA1_WITH_RSA 1.2.840.113549.1.1.5 +PKIX1_DSA 1.2.840.10040.4.1 +PKIX1_SHA1_WITH_DSA 1.2.840.10040.4.3 PKCS7_DATA 1.2.840.113549.1.7.1 PKCS7_SIGNED_DATA 1.2.840.113549.1.7.2 @@ -18,6 +22,5 @@ PKCS12_BAG_PKCS8_ENCRYPTED_KEY 1.2.840.113549.1.12.10.1.2 PKCS12_BAG_CERTIFICATE 1.2.840.113549.1.12.10.1.3 PKCS12_BAG_CRL 1.2.840.113549.1.12.10.1.4 - ALT_NAME_XMPP_ADDR 1.3.6.1.5.5.7.8.5 ALT_NAME_DNS_SRV 1.3.6.1.5.5.7.8.7 diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am index 5e611f7..f711931 100644 --- a/gcr/tests/Makefile.am +++ b/gcr/tests/Makefile.am @@ -59,6 +59,7 @@ EXTRA_DIST = \ noinst_PROGRAMS = \ frob-certificate \ + frob-certificate-request \ frob-combo-selector \ frob-gnupg-selector \ frob-key \ @@ -72,3 +73,7 @@ noinst_PROGRAMS = \ frob_unlock_SOURCES = \ frob-unlock.c \ ../gcr-viewer-window.c + +frob_certificate_request_SOURCES = \ + frob-certificate-request.c \ + console-interaction.c console-interaction.h diff --git a/gcr/tests/console-interaction.c b/gcr/tests/console-interaction.c new file mode 100644 index 0000000..6d75207 --- /dev/null +++ b/gcr/tests/console-interaction.c @@ -0,0 +1,175 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2011 Collabora, Ltd. + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include <glib.h> +#include <string.h> + +#ifdef G_OS_WIN32 +#include <glib/gprintf.h> +#include <conio.h> +#endif + +#include "console-interaction.h" + +/* + * WARNING: This is not the example you're looking for [slow hand wave]. This + * is not industrial strength, it's just for testing. It uses embarassing + * functions like getpass() and does lazy things with threads. + */ + +#define TYPE_CONSOLE_INTERACTION (console_interaction_get_type ()) +#define CONSOLE_INTERACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_CONSOLE_INTERACTION, ConsoleInteraction)) +#define CONSOLE_INTERACTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TYPE_CONSOLE_INTERACTION, ConsoleInteractionClass)) +#define IS_CONSOLE_INTERACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_CONSOLE_INTERACTION)) +#define IS_CONSOLE_INTERACTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TYPE_CONSOLE_INTERACTION)) +#define CONSOLE_INTERACTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_CONSOLE_INTERACTION, ConsoleInteractionClass)) + +typedef struct _ConsoleInteraction ConsoleInteraction; +typedef struct _ConsoleInteractionClass ConsoleInteractionClass; + +struct _ConsoleInteraction +{ + GTlsInteraction parent_instance; +}; + +struct _ConsoleInteractionClass +{ + GTlsInteractionClass parent_class; +}; + +GType console_interaction_get_type (void) G_GNUC_CONST; + +G_DEFINE_TYPE (ConsoleInteraction, console_interaction, G_TYPE_TLS_INTERACTION); + +#ifdef G_OS_WIN32 +/* win32 doesn't have getpass() */ +static gchar * +getpass (const gchar *prompt) +{ + static gchar buf[BUFSIZ]; + gint i; + + g_printf ("%s", prompt); + fflush (stdout); + + for (i = 0; i < BUFSIZ - 1; ++i) + { + buf[i] = _getch (); + if (buf[i] == '\r') + break; + } + buf[i] = '\0'; + + g_printf ("\n"); + + return &buf[0]; +} +#endif + +static GTlsInteractionResult +console_interaction_ask_password (GTlsInteraction *interaction, + GTlsPassword *password, + GCancellable *cancellable, + GError **error) +{ + const gchar *value; + gchar *prompt; + + prompt = g_strdup_printf ("Password \"%s\"': ", g_tls_password_get_description (password)); + value = getpass (prompt); + g_free (prompt); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return G_TLS_INTERACTION_FAILED; + + g_tls_password_set_value (password, (guchar *)value, -1); + return G_TLS_INTERACTION_HANDLED; +} + +static void +ask_password_with_getpass (GSimpleAsyncResult *res, + GObject *object, + GCancellable *cancellable) +{ + GTlsPassword *password; + GError *error = NULL; + + password = g_simple_async_result_get_op_res_gpointer (res); + console_interaction_ask_password (G_TLS_INTERACTION (object), password, + cancellable, &error); + if (error != NULL) + g_simple_async_result_take_error (res, error); +} + +static void +console_interaction_ask_password_async (GTlsInteraction *interaction, + GTlsPassword *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *res; + + res = g_simple_async_result_new (G_OBJECT (interaction), callback, user_data, + console_interaction_ask_password); + g_simple_async_result_set_op_res_gpointer (res, g_object_ref (password), g_object_unref); + g_simple_async_result_run_in_thread (res, ask_password_with_getpass, + G_PRIORITY_DEFAULT, cancellable); + g_object_unref (res); +} + +static GTlsInteractionResult +console_interaction_ask_password_finish (GTlsInteraction *interaction, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (interaction), + console_interaction_ask_password), G_TLS_INTERACTION_FAILED); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return G_TLS_INTERACTION_FAILED; + + return G_TLS_INTERACTION_HANDLED; +} + +static void +console_interaction_init (ConsoleInteraction *interaction) +{ + +} + +static void +console_interaction_class_init (ConsoleInteractionClass *klass) +{ + GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass); + interaction_class->ask_password = console_interaction_ask_password; + interaction_class->ask_password_async = console_interaction_ask_password_async; + interaction_class->ask_password_finish = console_interaction_ask_password_finish; +} + +GTlsInteraction * +console_interaction_new (void) +{ + return g_object_new (TYPE_CONSOLE_INTERACTION, NULL); +} diff --git a/gcr/tests/console-interaction.h b/gcr/tests/console-interaction.h new file mode 100644 index 0000000..b767421 --- /dev/null +++ b/gcr/tests/console-interaction.h @@ -0,0 +1,34 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2011 Collabora, Ltd. + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#ifndef __CONSOLE_INTERACTION_H__ +#define __CONSOLE_INTERACTION_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +GTlsInteraction * console_interaction_new (void); + +G_END_DECLS + +#endif /* __G_TLS_CONSOLE_INTERACTION_H__ */ diff --git a/gcr/tests/frob-certificate-request.c b/gcr/tests/frob-certificate-request.c new file mode 100644 index 0000000..a13a553 --- /dev/null +++ b/gcr/tests/frob-certificate-request.c @@ -0,0 +1,114 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Stef Walter <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include "console-interaction.h" + +#include "gcr/gcr-base.h" + +#include "egg/egg-armor.h" + +#include <gtk/gtk.h> + +#include <unistd.h> +#include <string.h> +#include <errno.h> + +const gchar *cn_name = NULL; + +static GckObject * +load_key_for_uri (const gchar *uri) +{ + GError *error = NULL; + GTlsInteraction *interaction; + GckEnumerator *enumerator; + GList *modules; + GckObject *key; + + gcr_pkcs11_initialize (NULL, &error); + g_assert_no_error (error); + + modules = gcr_pkcs11_get_modules (); + enumerator = gck_modules_enumerate_uri (modules, uri, GCK_SESSION_LOGIN_USER | + GCK_SESSION_READ_ONLY, &error); + gck_list_unref_free (modules); + + interaction = console_interaction_new (); + gck_enumerator_set_interaction (enumerator, interaction); + g_object_unref (interaction); + + key = gck_enumerator_next (enumerator, NULL, &error); + g_assert_no_error (error); + g_object_unref (enumerator); + + return key; +} + +static void +test_request (const gchar *uri) +{ + GcrCertificateRequest *req; + GError *error = NULL; + GckObject *key; + guchar *data, *output; + gsize n_data, n_output; + + key = load_key_for_uri (uri); + if (key == NULL) + g_error ("couldn't find key for uri: %s", uri); + + req = gcr_certificate_request_prepare (GCR_CERTIFICATE_REQUEST_PKCS10, key); + g_object_unref (key); + + gcr_certificate_request_set_cn (req, cn_name); + gcr_certificate_request_complete (req, NULL, &error); + g_assert_no_error (error); + + data = gcr_certificate_request_get_der_data (req, &n_data); + + output = egg_armor_write (data, n_data, + g_quark_from_static_string ("CERTIFICATE REQUEST"), + NULL, &n_output); + + g_free (data); + + write (1, output, n_output); + g_free (output); +} + +int +main(int argc, char *argv[]) +{ + gtk_init (&argc, &argv); + g_set_prgname ("frob-certificate-request"); + + if (argc <= 1) + g_printerr ("frob-certificate-request: specify pkcs11: url of key"); + + if (cn_name == NULL) + cn_name = g_strdup ("name.example.com"); + + test_request (argv[1]); + return 0; +} |