4
* Copyright (C) 2008 Stefan Walter
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as
8
* published by the Free Software Foundation; either version 2.1 of
9
* the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24
#include "gcr-import-dialog.h"
25
#include "gcr-importer.h"
26
#include "gcr-internal.h"
27
#include "gcr-marshal.h"
28
#include "gcr-parser.h"
30
#include <glib/gi18n-lib.h>
44
static guint signals[LAST_SIGNAL] = { 0 };
46
struct _GcrImporterPrivate {
49
GcrImporterPromptBehavior behavior;
51
/* Information about last import */
55
/* State data during import */
64
/* Extra async stuff */
65
GAsyncReadyCallback callback;
69
/* State forward declarations */
70
static void state_cancelled (GcrImporter *self, gboolean async);
71
static void state_complete (GcrImporter *self, gboolean async);
72
static void state_create_object (GcrImporter *self, gboolean async);
73
static void state_open_session (GcrImporter *self, gboolean async);
74
static void state_initialize_pin (GcrImporter *self, gboolean async);
76
static void gcr_importer_async_result (GAsyncResultIface *iface);
77
G_DEFINE_TYPE_WITH_CODE (GcrImporter, gcr_importer, G_TYPE_OBJECT,
78
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_importer_async_result));
82
/* -----------------------------------------------------------------------------
87
cleanup_state_data (GcrImporter *self)
92
g_byte_array_free (self->pv->buffer, TRUE);
93
self->pv->buffer = NULL;
95
if (self->pv->session)
96
g_object_unref (self->pv->session);
97
self->pv->session = NULL;
99
while ((attrs = g_queue_pop_head (&self->pv->queue)) != NULL)
100
gck_attributes_unref (attrs);
101
g_assert (g_queue_is_empty (&self->pv->queue));
103
if (self->pv->cancel)
104
g_object_unref (self->pv->cancel);
105
self->pv->cancel = NULL;
109
cleanup_import_data (GcrImporter *self)
112
g_clear_error (&self->pv->error);
113
self->pv->succeeded = TRUE;
117
next_state (GcrImporter *self, void (*state) (GcrImporter*, gboolean))
119
g_assert (GCR_IS_IMPORTER (self));
120
g_assert (self->pv->processing);
123
if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
124
state = state_cancelled;
126
(state) (self, self->pv->async);
130
prepare_auth_primary (CK_OBJECT_CLASS klass)
132
if (klass == CKO_PRIVATE_KEY)
133
return _("Enter password to unlock the private key");
134
else if (klass == CKO_CERTIFICATE)
135
return _("Enter password to unlock the certificate");
137
return _("Enter password to unlock");
141
prepare_auth_secondary (CK_OBJECT_CLASS klass, const gchar *label)
144
if (klass == CKO_PRIVATE_KEY) {
145
/* TRANSLATORS: The key is locked. */
146
return g_strdup (_("In order to import the private key, it must be unlocked"));
147
} else if (klass == CKO_CERTIFICATE) {
148
/* TRANSLATORS: The certificate is locked. */
149
return g_strdup (_("In order to import the certificate, it must be unlocked"));
151
/* TRANSLATORS: The data is locked. */
152
return g_strdup (_("In order to import the data, it must be unlocked"));
155
if (klass == CKO_PRIVATE_KEY) {
156
/* TRANSLATORS: The key is locked. */
157
return g_strdup_printf (_("In order to import the private key '%s', it must be unlocked"), label);
158
} else if (klass == CKO_CERTIFICATE) {
159
/* TRANSLATORS: The certificate is locked. */
160
return g_strdup_printf (_("In order to import the certificate '%s', it must be unlocked"), label);
162
/* TRANSLATORS: The object '%s' is locked. */
163
return g_strdup_printf (_("In order to import '%s', it must be unlocked"), label);
169
on_parser_parsed (GcrParser *parser, GcrImporter *self)
171
GckAttributes *attrs;
173
g_return_if_fail (GCR_IS_PARSER (parser));
174
g_return_if_fail (GCR_IS_IMPORTER (self));
176
attrs = gcr_parser_get_parsed_attributes (parser);
177
g_return_if_fail (attrs);
179
gcr_importer_queue (self, gcr_parser_get_parsed_label (parser), attrs);
183
on_parser_authenticate (GcrParser *parser, gint count, GcrImporter *self)
185
GcrImportDialog *dialog;
186
GckAttributes *attrs;
187
const gchar *password;
192
dialog = _gcr_import_dialog_new ();
195
_gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
197
/* Figure out the text for the dialog */
198
attrs = gcr_parser_get_parsed_attributes (parser);
199
g_return_val_if_fail (attrs, FALSE);
201
if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
203
if (!gck_attributes_find_string (attrs, CKA_LABEL, &label))
206
text = prepare_auth_secondary (klass, label);
207
_gcr_import_dialog_set_primary_text (dialog, prepare_auth_primary (klass));
208
_gcr_import_dialog_set_secondary_text (dialog, text);
212
if (!_gcr_import_dialog_run (dialog, NULL))
215
slot = _gcr_import_dialog_get_selected_slot (dialog);
216
gcr_importer_set_slot (self, slot);
218
password = _gcr_import_dialog_get_password (dialog);
219
gcr_parser_add_password (parser, password);
221
g_object_unref (dialog);
222
self->pv->prompted = TRUE;
226
/* ---------------------------------------------------------------------------------
231
state_complete (GcrImporter *self, gboolean async)
233
if (async && self->pv->callback != NULL)
234
(self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self), self->pv->user_data);
236
cleanup_state_data (self);
237
self->pv->processing = FALSE;
241
state_failure (GcrImporter *self, gboolean async)
243
self->pv->succeeded = FALSE;
244
next_state (self, state_complete);
248
state_cancelled (GcrImporter *self, gboolean async)
250
if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel))
251
g_cancellable_cancel (self->pv->cancel);
253
g_error_free (self->pv->error);
254
self->pv->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled"));
255
next_state (self, state_failure);
258
/* ---------------------------------------------------------------------------------
263
complete_create_object (GcrImporter *self, GckObject *object, GError *error)
265
if (object == NULL) {
266
g_propagate_error (&self->pv->error, error);
267
next_state (self, state_failure);
270
g_signal_emit (self, signals[IMPORTED], 0, object);
271
g_object_unref (object);
272
next_state (self, state_create_object);
277
on_create_object (GObject *obj, GAsyncResult *res, gpointer user_data)
279
GError *error = NULL;
280
GckObject *object = gck_session_create_object_finish (GCK_SESSION (obj), res, &error);
281
complete_create_object (GCR_IMPORTER (user_data), object, error);
285
state_create_object (GcrImporter *self, gboolean async)
287
GckAttributes *attrs;
289
GError *error = NULL;
291
/* No more objects */
292
if (g_queue_is_empty (&self->pv->queue)) {
293
next_state (self, state_complete);
297
/* Pop first one off the list */
298
attrs = g_queue_pop_head (&self->pv->queue);
301
gck_attributes_add_boolean (attrs, CKA_TOKEN, CK_TRUE);
304
gck_session_create_object_async (self->pv->session, attrs, self->pv->cancel,
305
on_create_object, self);
307
object = gck_session_create_object (self->pv->session, attrs, self->pv->cancel, &error);
308
complete_create_object (self, object, error);
311
gck_attributes_unref (attrs);
315
/* ---------------------------------------------------------------------------------
320
complete_open_session (GcrImporter *self, GckSession *session, GError *error)
323
g_propagate_error (&self->pv->error, error);
324
next_state (self, state_failure);
326
self->pv->session = session;
327
next_state (self, state_create_object);
332
on_open_session (GObject *obj, GAsyncResult *res, gpointer user_data)
334
GError *error = NULL;
335
GckSession *session = gck_slot_open_session_finish (GCK_SLOT (obj), res, &error);
336
complete_open_session (GCR_IMPORTER (user_data), session, error);
340
state_open_session (GcrImporter *self, gboolean async)
343
GError *error = NULL;
345
if (!self->pv->slot) {
346
g_set_error (&self->pv->error, GCR_DATA_ERROR, GCR_ERROR_FAILURE, _("No location available to import to"));
347
next_state (self, state_failure);
352
gck_slot_open_session_async (self->pv->slot, GCK_SESSION_READ_WRITE, self->pv->cancel,
353
on_open_session, self);
355
session = gck_slot_open_session_full (self->pv->slot, GCK_SESSION_READ_WRITE, 0, NULL, NULL,
356
self->pv->cancel, &error);
357
complete_open_session (self, session, error);
362
/* ---------------------------------------------------------------------------------
365
* HACK: This is a big temporary hack to get, until the next version
366
* when we can fix this correctly.
370
hacky_perform_initialize_pin (GckSlot *slot)
372
CK_FUNCTION_LIST_PTR funcs;
373
CK_SESSION_HANDLE session;
378
* This hack only works when:
380
* - Module is protected authentication path
381
* - No other sessions are open.
383
* Thankfully this is the case with gnome-keyring-daemon and
384
* the gnome-keyring tool.
387
funcs = gck_module_get_functions (gck_slot_get_module (slot));
388
g_return_val_if_fail (funcs, CKR_GENERAL_ERROR);
389
slot_id = gck_slot_get_handle (slot);
391
rv = funcs->C_OpenSession (slot_id, CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL, NULL, &session);
395
rv = funcs->C_Login (session, CKU_SO, NULL, 0);
397
rv = funcs->C_InitPIN (session, NULL, 0);
398
funcs->C_Logout (session);
401
funcs->C_CloseSession (session);
407
state_initialize_pin (GcrImporter *self, gboolean async)
413
g_assert (GCR_IS_IMPORTER (self));
415
/* HACK: Doesn't function when async */
417
g_return_if_fail (self->pv->slot);
418
info = gck_slot_get_token_info (self->pv->slot);
419
g_return_if_fail (info);
421
initialize = !(info->flags & CKF_USER_PIN_INITIALIZED);
422
gck_token_info_free (info);
425
rv = hacky_perform_initialize_pin (self->pv->slot);
427
g_propagate_error (&self->pv->error, g_error_new (GCK_ERROR, rv, "%s", gck_message_from_rv (rv)));
428
next_state (self, state_failure);
434
next_state (self, state_open_session);
437
/* ---------------------------------------------------------------------------------
442
complete_import_prompt (GcrImporter *self, GcrImportDialog *dialog, gint response)
446
gtk_widget_hide (GTK_WIDGET (dialog));
447
self->pv->prompted = TRUE;
449
/* No dialog or dialog completed */
450
if (response == GTK_RESPONSE_OK) {
452
slot = _gcr_import_dialog_get_selected_slot (dialog);
453
gcr_importer_set_slot (self, slot);
454
next_state (self, state_initialize_pin);
456
/* The dialog was cancelled or closed */
458
next_state (self, state_cancelled);
463
on_prompt_response (GtkDialog *dialog, gint response, gpointer user_data)
465
complete_import_prompt (GCR_IMPORTER (user_data), GCR_IMPORT_DIALOG (dialog), response);
466
g_object_unref (dialog);
470
state_import_prompt (GcrImporter *self, gboolean async)
472
GcrImportDialog *dialog;
476
g_assert (GCR_IS_IMPORTER (self));
478
/* No need to prompt */
479
if (self->pv->prompted == TRUE)
481
else if (self->pv->behavior == GCR_IMPORTER_PROMPT_ALWAYS)
483
else if (self->pv->behavior == GCR_IMPORTER_PROMPT_NEVER)
486
prompt = self->pv->slot ? FALSE : TRUE;
488
if (prompt == FALSE) {
489
next_state (self, state_initialize_pin);
493
dialog = _gcr_import_dialog_new ();
495
_gcr_import_dialog_set_primary_text (dialog, _("Import Certificates/Keys"));
496
_gcr_import_dialog_hide_password (dialog);
498
if (self->pv->slot) {
499
_gcr_import_dialog_set_selected_slot (dialog, self->pv->slot);
500
_gcr_import_dialog_hide_selected_slot (dialog);
502
_gcr_import_dialog_set_secondary_text (dialog, _("Choose a location to store the imported certificates/keys."));
505
/* Prompt without blocking main loop */
507
g_signal_connect (dialog, "response", G_CALLBACK (on_prompt_response), self);
508
gtk_widget_show (GTK_WIDGET (dialog));
512
response = gtk_dialog_run (GTK_DIALOG (dialog));
513
complete_import_prompt (self, dialog, response);
514
g_object_unref (dialog);
519
/* -----------------------------------------------------------------------------
524
gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props)
526
GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props));
527
g_return_val_if_fail (self, NULL);
529
return G_OBJECT (self);
533
gcr_importer_init (GcrImporter *self)
535
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORTER, GcrImporterPrivate);
536
self->pv->behavior = GCR_IMPORTER_PROMPT_NEEDED;
537
g_queue_init (&self->pv->queue);
541
gcr_importer_dispose (GObject *obj)
543
GcrImporter *self = GCR_IMPORTER (obj);
545
cleanup_state_data (self);
546
cleanup_import_data (self);
548
if (self->pv->parser)
549
g_object_unref (self->pv->parser);
550
self->pv->parser = NULL;
553
g_object_unref (self->pv->slot);
554
self->pv->slot = NULL;
556
G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj);
560
gcr_importer_finalize (GObject *obj)
562
GcrImporter *self = GCR_IMPORTER (obj);
564
g_assert (!self->pv->parser);
565
g_assert (!self->pv->slot);
567
G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj);
571
gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value,
574
GcrImporter *self = GCR_IMPORTER (obj);
578
gcr_importer_set_slot (self, g_value_get_object (value));
580
case PROP_PROMPT_BEHAVIOR:
581
gcr_importer_set_prompt_behavior (self, (GcrImporterPromptBehavior)g_value_get_int (value));
584
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
590
gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value,
593
GcrImporter *self = GCR_IMPORTER (obj);
597
g_value_set_object (value, gcr_importer_get_slot (self));
599
case PROP_PROMPT_BEHAVIOR:
600
g_value_set_int (value, gcr_importer_get_prompt_behavior (self));
603
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
609
gcr_importer_class_init (GcrImporterClass *klass)
611
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
613
gobject_class->constructor = gcr_importer_constructor;
614
gobject_class->dispose = gcr_importer_dispose;
615
gobject_class->finalize = gcr_importer_finalize;
616
gobject_class->set_property = gcr_importer_set_property;
617
gobject_class->get_property = gcr_importer_get_property;
619
g_type_class_add_private (gobject_class, sizeof (GcrImporterPrivate));
621
g_object_class_install_property (gobject_class, PROP_SLOT,
622
g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into",
623
GCK_TYPE_SLOT, G_PARAM_READWRITE));
625
g_object_class_install_property (gobject_class, PROP_PROMPT_BEHAVIOR,
626
g_param_spec_int ("prompt-behavior", "Prompt Behavior", "Import Prompt Behavior",
627
0, G_MAXINT, GCR_IMPORTER_PROMPT_NEEDED, G_PARAM_READWRITE));
629
signals[QUEUED] = g_signal_new ("queued", GCR_TYPE_IMPORTER,
630
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, queued),
631
NULL, NULL, _gcr_marshal_VOID__STRING_BOXED,
632
G_TYPE_NONE, 1, G_TYPE_STRING, GCK_TYPE_ATTRIBUTES);
634
signals[IMPORTED] = g_signal_new ("imported", GCR_TYPE_IMPORTER,
635
G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, imported),
636
NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
637
G_TYPE_NONE, 1, GCK_TYPE_OBJECT);
643
gcr_importer_real_get_user_data (GAsyncResult *base)
645
g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
646
return GCR_IMPORTER (base)->pv->user_data;
650
gcr_importer_real_get_source_object (GAsyncResult *base)
652
g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL);
653
return G_OBJECT (base);
657
gcr_importer_async_result (GAsyncResultIface *iface)
659
iface->get_source_object = gcr_importer_real_get_source_object;
660
iface->get_user_data = gcr_importer_real_get_user_data;
663
/* -----------------------------------------------------------------------------
668
gcr_importer_new (void)
670
return g_object_new (GCR_TYPE_IMPORTER, NULL);
674
gcr_importer_get_slot (GcrImporter *self)
676
g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL);
677
return self->pv->slot;
681
gcr_importer_set_slot (GcrImporter *self, GckSlot *slot)
683
g_return_if_fail (GCR_IS_IMPORTER (self));
688
g_object_unref (self->pv->slot);
689
self->pv->slot = slot;
690
g_object_notify (G_OBJECT (self), "slot");
693
GcrImporterPromptBehavior
694
gcr_importer_get_prompt_behavior (GcrImporter *self)
696
g_return_val_if_fail (GCR_IS_IMPORTER (self), GCR_IMPORTER_PROMPT_NEEDED);
697
return self->pv->behavior;
701
gcr_importer_set_prompt_behavior (GcrImporter *self, GcrImporterPromptBehavior behavior)
703
g_return_if_fail (GCR_IMPORTER (self));
704
self->pv->behavior = behavior;
705
g_object_notify (G_OBJECT (self), "prompt-behavior");
709
gcr_importer_import (GcrImporter *self, GCancellable *cancel, GError **error)
711
g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
712
g_return_val_if_fail (!error || !*error, FALSE);
713
g_return_val_if_fail (!self->pv->processing, FALSE);
715
cleanup_import_data (self);
718
self->pv->cancel = g_object_ref (cancel);
719
self->pv->processing = TRUE;
720
self->pv->async = FALSE;
722
next_state (self, state_import_prompt);
724
g_assert (!self->pv->processing);
725
g_assert (!self->pv->cancel);
727
if (!self->pv->succeeded) {
728
g_propagate_error (error, self->pv->error);
729
self->pv->error = NULL;
737
gcr_importer_import_async (GcrImporter *self, GCancellable *cancel,
738
GAsyncReadyCallback callback, gpointer user_data)
740
g_return_if_fail (GCR_IS_IMPORTER (self));
741
g_return_if_fail (!self->pv->processing);
743
cleanup_import_data (self);
746
self->pv->cancel = g_object_ref (cancel);
747
self->pv->processing = TRUE;
748
self->pv->async = TRUE;
749
self->pv->callback = callback;
750
self->pv->user_data = user_data;
752
next_state (self, state_import_prompt);
753
g_assert (self->pv->processing);
757
gcr_importer_import_finish (GcrImporter *self, GAsyncResult *res, GError **error)
759
g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
760
g_return_val_if_fail (GCR_IMPORTER (res) == self, FALSE);
761
g_return_val_if_fail (!error || !*error, FALSE);
762
g_return_val_if_fail (!self->pv->processing, FALSE);
764
g_assert (!self->pv->cancel);
766
if (!self->pv->succeeded) {
767
g_propagate_error (error, self->pv->error);
768
self->pv->error = NULL;
777
gcr_importer_listen (GcrImporter *self, GcrParser *parser)
779
g_return_if_fail (GCR_IS_IMPORTER (self));
780
g_return_if_fail (GCR_IS_PARSER (self));
782
/* Listen in to the parser */
783
g_signal_connect_object (parser, "parsed", G_CALLBACK (on_parser_parsed), self, 0);
784
g_signal_connect_object (parser, "authenticate", G_CALLBACK (on_parser_authenticate), self, 0);
788
gcr_importer_queue (GcrImporter *self, const gchar *label, GckAttributes *attrs)
790
g_return_if_fail (GCR_IS_IMPORTER (self));
791
g_return_if_fail (attrs);
793
g_queue_push_tail (&self->pv->queue, gck_attributes_ref (attrs));
794
g_signal_emit (self, signals[QUEUED], 0, label, attrs);
797
#ifndef GCR_DISABLE_DEPRECATED
800
gcr_importer_get_parser (GcrImporter *self)
802
g_warning ("gcr_importer_get_parser() is no longer supported "
803
"Use gcr_importer_listen() instead.");
808
gcr_importer_set_parser (GcrImporter *self, GcrParser *parser)
810
g_warning ("gcr_importer_set_parser() is no longer supported "
811
"Use gcr_importer_listen() instead.");
814
#endif /* GCR_DISABLE_DEPRECATED */