diff options
Diffstat (limited to 'gcr')
-rw-r--r-- | gcr/Makefile.am | 50 | ||||
-rw-r--r-- | gcr/gcr-import-dialog.c | 452 | ||||
-rw-r--r-- | gcr/gcr-import-dialog.glade | 185 | ||||
-rw-r--r-- | gcr/gcr-import-dialog.h | 86 | ||||
-rw-r--r-- | gcr/gcr-importer.c | 749 | ||||
-rw-r--r-- | gcr/gcr-importer.h | 59 | ||||
-rw-r--r-- | gcr/gcr-internal.h | 8 | ||||
-rw-r--r-- | gcr/gcr-library.c (renamed from gcr/gcr-internal.c) | 105 | ||||
-rw-r--r-- | gcr/gcr-parser.c | 163 | ||||
-rw-r--r-- | gcr/gcr-parser.h | 44 | ||||
-rw-r--r-- | gcr/gcr-types.h | 19 | ||||
-rw-r--r-- | gcr/gcr.h | 46 | ||||
-rw-r--r-- | gcr/tests/Makefile.am | 4 | ||||
-rw-r--r-- | gcr/tests/unit-test-parser.c | 2 |
14 files changed, 1630 insertions, 342 deletions
diff --git a/gcr/Makefile.am b/gcr/Makefile.am index b1cef7d..0edd1de 100644 --- a/gcr/Makefile.am +++ b/gcr/Makefile.am @@ -1,12 +1,24 @@ -incdir = $(includedir)/gcr +# ------------------------------------------------------------------ +# UI BUILDER +# -inc_HEADERS = \ - gcr-parser.h \ - gcr-types.h +uidir = $(datadir)/gcr/ui/ + +GLADE_FILES = \ + gcr-import-dialog.glade + +.glade.ui: + gtk-builder-convert --skip-windows $< $@ + +ui_DATA = $(GLADE_FILES:.glade=.ui) + +# ------------------------------------------------------------------ +# LIBRARY INCLUDES = \ -I$(top_builddir) \ -I$(top_srcdir) \ + $(GTK_CFLAGS) \ $(GOBJECT_CFLAGS) \ $(GLIB_CFLAGS) @@ -16,17 +28,26 @@ BUILT_SOURCES = \ lib_LTLIBRARIES = libgcr.la libgcr_la_SOURCES = \ - gcr-internal.c gcr-internal.h \ + gcr-import-dialog.c gcr-import-dialog.h \ + gcr-importer.c gcr-importer.h \ + gcr-internal.h \ + gcr-library.c \ gcr-parser.c gcr-parser.h \ gcr-types.h \ $(BUILT_SOURCES) +libgcr_la_CFLAGS = \ + -DPKCS11_MODULE_PATH=\""$(libdir)/gnome-keyring/gnome-keyring-pkcs11.so"\" \ + -DGCR_API_SUBJECT_TO_CHANGE \ + -DUIDIR=\""$(uidir)"\" + libgcr_la_LDFLAGS = \ -version-info $(GCR_LT_RELEASE) \ -no-undefined -export-symbols-regex 'gcr_*' libgcr_la_LIBADD = \ $(top_builddir)/egg/libegg.la \ + $(top_builddir)/egg/libegg-secure-entry.la \ $(top_builddir)/gp11/libgp11.la \ $(GOBJECT_LIBS) \ $(GLIB_LIBS) @@ -41,16 +62,24 @@ gcr-marshal.c: gcr-marshal.list $(GLIB_GENMARSHAL) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = gcr-$(GCR_MAJOR).pc +gcr-$(GCR_MAJOR).pc: gcr.pc + cp gcr.pc gcr-$(GCR_MAJOR).pc + +# ---------------------------------------------------------------- + EXTRA_DIST = \ gcr.pc.in \ gcr-marshal.list \ - gcr-import-dialog.glade + gcr-import-dialog.glade \ + $(GLADE_FILES) -DISTCLEANFILES = \ - gcr-$(GCR_MAJOR).pc +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(ui_DATA) \ + $(pkgconfig_DATA) -gcr-$(GCR_MAJOR).pc: gcr.pc - cp gcr.pc gcr-$(GCR_MAJOR).pc +DISTCLEANFILES = \ + $(pkgconfig_DATA) if WITH_TESTS TESTS_DIR = tests @@ -60,4 +89,3 @@ endif SUBDIRS = . \ $(TESTS_DIR) - diff --git a/gcr/gcr-import-dialog.c b/gcr/gcr-import-dialog.c new file mode 100644 index 0000000..b56b883 --- /dev/null +++ b/gcr/gcr-import-dialog.c @@ -0,0 +1,452 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * 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. + */ + +#include "config.h" + +#include "gcr-import-dialog.h" +#include "gcr-internal.h" + +#include "egg/egg-secure-entry.h" + +enum { + PROP_0, + PROP_SELECTED_SLOT, + PROP_PASSWORD, + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT +}; + +enum { + COLUMN_SLOT, + COLUMN_ICON, + COLUMN_LABEL, + N_COLUMNS +}; + +struct _GcrImportDialogPrivate { + GtkBuilder *builder; + EggSecureEntry *entry; + GtkComboBox *combo; + GtkListStore *slots; +}; + +G_DEFINE_TYPE (GcrImportDialog, _gcr_import_dialog, GTK_TYPE_DIALOG); + +/* ----------------------------------------------------------------------------- + * INTERNAL + */ + +static void +populate_slots (GcrImportDialog *self) +{ + GList *modules, *m; + GList *slots, *s; + GtkTreeIter iter; + GP11TokenInfo *info; + gboolean added; + + g_assert (GCR_IS_IMPORT_DIALOG (self)); + + if (self->pv->slots) + return; + + self->pv->slots = gtk_list_store_new (N_COLUMNS, GP11_TYPE_SLOT, G_TYPE_STRING, G_TYPE_STRING); + gtk_combo_box_set_model (self->pv->combo, GTK_TREE_MODEL (self->pv->slots)); + + modules = _gcr_get_pkcs11_modules (); + g_return_if_fail (modules); + + gtk_list_store_clear (self->pv->slots); + + added = FALSE; + for (m = modules; m; m = g_list_next (m)) { + + g_return_if_fail (GP11_IS_MODULE (m->data)); + slots = gp11_module_get_slots (m->data, TRUE); + + for (s = slots; s; s = g_list_next (s)) { + info = gp11_slot_get_token_info (s->data); + if (!(info->flags & CKF_WRITE_PROTECTED)) { + gtk_list_store_append (self->pv->slots, &iter); + gtk_list_store_set (self->pv->slots, &iter, + COLUMN_LABEL, info->label, + COLUMN_SLOT, s->data, + -1); + added = TRUE; + } + } + + gp11_list_unref_free (slots); + } + + if (added) + gtk_combo_box_set_active (self->pv->combo, 0); +} + +/* ----------------------------------------------------------------------------- + * OBJECT + */ + +static void +gcr_import_dialog_real_realize (GtkWidget *base) +{ + GcrImportDialog *self = GCR_IMPORT_DIALOG (base); + if (GTK_WIDGET_VISIBLE (self->pv->combo)) + populate_slots (self); + GTK_WIDGET_CLASS (_gcr_import_dialog_parent_class)->realize (base); +} + +static GObject* +gcr_import_dialog_constructor (GType type, guint n_props, GObjectConstructParam *props) +{ + GcrImportDialog *self = GCR_IMPORT_DIALOG (G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->constructor(type, n_props, props)); + GtkCellRenderer *renderer; + GtkWidget *widget; + + g_return_val_if_fail (self, NULL); + + if (!gtk_builder_add_from_file (self->pv->builder, UIDIR "gcr-import-dialog.ui", NULL)) + g_return_val_if_reached (NULL); + + /* Fill in the dialog from builder */ + widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "import-dialog")); + g_return_val_if_fail (widget, FALSE); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (self)->vbox), widget); + + /* Add a secure entry */ + self->pv->entry = EGG_SECURE_ENTRY (egg_secure_entry_new ()); + widget = GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area")); + gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (self->pv->entry)); + gtk_widget_show (GTK_WIDGET (self->pv->entry)); + + /* Initialize the combo box */ + self->pv->combo = GTK_COMBO_BOX (gtk_builder_get_object (self->pv->builder, "slot-combo")); + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->pv->combo), renderer, FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->pv->combo), renderer, "icon-name", COLUMN_ICON); + g_object_set (renderer, "xpad", 3, NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self->pv->combo), renderer, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self->pv->combo), renderer, "text", COLUMN_LABEL); + + /* Add our various buttons */ + gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK); + + return G_OBJECT (self); +} + +static void +_gcr_import_dialog_init (GcrImportDialog *self) +{ + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORT_DIALOG, GcrImportDialogPrivate); + self->pv->builder = gtk_builder_new (); +} + +static void +gcr_import_dialog_dispose (GObject *obj) +{ + G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->dispose (obj); +} + +static void +gcr_import_dialog_finalize (GObject *obj) +{ + GcrImportDialog *self = GCR_IMPORT_DIALOG (obj); + + g_object_unref (self->pv->slots); + self->pv->slots = NULL; + + g_object_unref (self->pv->builder); + self->pv->builder = NULL; + + G_OBJECT_CLASS (_gcr_import_dialog_parent_class)->finalize (obj); +} + +static void +gcr_import_dialog_set_property (GObject *obj, guint prop_id, const GValue *value, + GParamSpec *pspec) +{ + GcrImportDialog *self = GCR_IMPORT_DIALOG (obj); + + switch (prop_id) { + case PROP_SELECTED_SLOT: + _gcr_import_dialog_set_selected_slot (self, g_value_get_object (value)); + break; + case PROP_PASSWORD: + _gcr_import_dialog_set_password (self, g_value_get_pointer (value)); + break; + case PROP_PRIMARY_TEXT: + _gcr_import_dialog_set_primary_text (self, g_value_get_string (value)); + break; + case PROP_SECONDARY_TEXT: + _gcr_import_dialog_set_secondary_text (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +gcr_import_dialog_get_property (GObject *obj, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + GcrImportDialog *self = GCR_IMPORT_DIALOG (obj); + + switch (prop_id) { + case PROP_SELECTED_SLOT: + g_value_set_object (value, _gcr_import_dialog_get_selected_slot (self)); + break; + case PROP_PASSWORD: + g_value_set_pointer (value, (gpointer)_gcr_import_dialog_get_password (self)); + break; + case PROP_PRIMARY_TEXT: + g_value_set_string (value, _gcr_import_dialog_get_primary_text (self)); + break; + case PROP_SECONDARY_TEXT: + g_value_set_string (value, _gcr_import_dialog_get_secondary_text (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); + break; + } +} + +static void +_gcr_import_dialog_class_init (GcrImportDialogClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->constructor = gcr_import_dialog_constructor; + gobject_class->dispose = gcr_import_dialog_dispose; + gobject_class->finalize = gcr_import_dialog_finalize; + gobject_class->set_property = gcr_import_dialog_set_property; + gobject_class->get_property = gcr_import_dialog_get_property; + + widget_class->realize = gcr_import_dialog_real_realize; + + g_type_class_add_private (gobject_class, sizeof (GcrImportDialogPrivate)); + + g_object_class_install_property (gobject_class, PROP_SELECTED_SLOT, + g_param_spec_object ("selected-slot", "Selected Slot", "Selected PKCS#11 slot", + GP11_TYPE_SLOT, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_PASSWORD, + g_param_spec_pointer ("password", "Password", "Pointer to password", + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_PRIMARY_TEXT, + g_param_spec_string ("primary-text", "Primary Text", "Primary dialog text", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_SECONDARY_TEXT, + g_param_spec_string ("secondary-text", "Secondary Text", "Dialog secondary text", + NULL, G_PARAM_READWRITE)); + + _gcr_initialize (); +} + +/* ----------------------------------------------------------------------------- + * PUBLIC + */ + +GcrImportDialog* +_gcr_import_dialog_new (void) +{ + GcrImportDialog *dialog = g_object_new (GCR_TYPE_IMPORT_DIALOG, NULL); + return g_object_ref_sink (dialog); +} + +gboolean +_gcr_import_dialog_run (GcrImportDialog *self, GtkWindow *parent) +{ + gboolean ret; + + g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), FALSE); + + if (parent != NULL) + gtk_window_set_transient_for (GTK_WINDOW (self), parent); + + ret = (gtk_dialog_run (GTK_DIALOG (self)) == GTK_RESPONSE_OK); + + if (parent != NULL) + gtk_window_set_transient_for (GTK_WINDOW (self), NULL); + + gtk_widget_hide (GTK_WIDGET (self)); + return ret; +} + +GP11Slot* +_gcr_import_dialog_get_selected_slot (GcrImportDialog *self) +{ + GtkTreeIter iter; + GP11Slot *slot; + + g_return_val_if_fail (GCR_IMPORT_DIALOG (self), NULL); + + if (GTK_WIDGET_VISIBLE (self->pv->combo)) + populate_slots (self); + else + return NULL; + + if (!gtk_combo_box_get_active_iter (self->pv->combo, &iter)) + return NULL; + + gtk_tree_model_get (GTK_TREE_MODEL (self->pv->slots), &iter, COLUMN_SLOT, &slot, -1); + + /* We hold the reference to this */ + if (slot != NULL) + g_object_unref (slot); + + return slot; +} + +void +_gcr_import_dialog_set_selected_slot (GcrImportDialog *self, GP11Slot *slot) +{ + GtkTreeIter iter; + GP11Slot *it_slot; + gboolean matched; + + g_return_if_fail (GCR_IMPORT_DIALOG (self)); + + if (GTK_WIDGET_VISIBLE (self->pv->combo)) + populate_slots (self); + else + g_return_if_reached (); + + if (slot == NULL) { + gtk_combo_box_set_active (self->pv->combo, -1); + return; + } + + g_return_if_fail (GP11_IS_SLOT (slot)); + + matched = FALSE; + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->pv->slots), &iter)) { + do { + gtk_tree_model_get (GTK_TREE_MODEL (self->pv->slots), &iter, COLUMN_SLOT, &it_slot, -1); + if (gp11_slot_equal (it_slot, slot)) + matched = TRUE; + g_object_unref (it_slot); + } while (!matched && gtk_tree_model_iter_next (GTK_TREE_MODEL (self->pv->slots), &iter)); + } + + if (matched) { + gtk_combo_box_set_active_iter (self->pv->combo, &iter); + } else { + gtk_combo_box_set_active (self->pv->combo, -1); + g_return_if_reached (); + } +} + +void +_gcr_import_dialog_show_selected_slot (GcrImportDialog *self) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-area"))); +} + +void +_gcr_import_dialog_hide_selected_slot (GcrImportDialog *self) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-label"))); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "slot-area"))); +} + +const gchar* +_gcr_import_dialog_get_password (GcrImportDialog *self) +{ + g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL); + return egg_secure_entry_get_text (self->pv->entry); +} + +void +_gcr_import_dialog_set_password (GcrImportDialog *self, const gchar *password) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + if (password == NULL) + password = ""; + egg_secure_entry_set_text (self->pv->entry, password); +} + +void +_gcr_import_dialog_show_password (GcrImportDialog *self) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-label"))); + gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area"))); +} + +void +_gcr_import_dialog_hide_password (GcrImportDialog *self) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-label"))); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (self->pv->builder, "password-area"))); +} + +const gchar* +_gcr_import_dialog_get_primary_text (GcrImportDialog *self) +{ + g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL); + return gtk_label_get_text (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "primary-text"))); +} + +void +_gcr_import_dialog_set_primary_text (GcrImportDialog *self, const gchar *text) +{ + gchar *label; + + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + + if (text == NULL) + text = ""; + + label = g_markup_printf_escaped ("<span size='large' weight='bold'>%s</span>", text); + gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "primary-text")), label); + g_free (label); + + g_object_notify (G_OBJECT (self), "primary-text"); +} + +const gchar* +_gcr_import_dialog_get_secondary_text (GcrImportDialog *self) +{ + g_return_val_if_fail (GCR_IS_IMPORT_DIALOG (self), NULL); + return gtk_label_get_text (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text"))); +} + +void +_gcr_import_dialog_set_secondary_text (GcrImportDialog *self, const gchar *text) +{ + g_return_if_fail (GCR_IS_IMPORT_DIALOG (self)); + + if (text == NULL) + text = ""; + + gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (self->pv->builder, "secondary-text")), text); + g_object_notify (G_OBJECT (self), "primary-text"); +} diff --git a/gcr/gcr-import-dialog.glade b/gcr/gcr-import-dialog.glade index 7da00a1..e91c662 100644 --- a/gcr/gcr-import-dialog.glade +++ b/gcr/gcr-import-dialog.glade @@ -1,164 +1,127 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.5 on Sat Jan 17 14:53:28 2009 --> +<!--Generated with glade3 3.4.5 on Wed Jan 21 16:01:29 2009 --> <glade-interface> - <widget class="GtkDialog" id="dialog1"> - <property name="border_width">5</property> - <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> - <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> - <property name="has_separator">False</property> - <child internal-child="vbox"> - <widget class="GtkVBox" id="dialog-vbox1"> + <widget class="GtkWindow" id="window1"> + <child> + <widget class="GtkVBox" id="import-dialog"> <property name="visible">True</property> - <property name="spacing">2</property> + <property name="border_width">6</property> + <property name="spacing">6</property> <child> - <widget class="GtkVBox" id="vbox1"> + <widget class="GtkHBox" id="hbox2"> <property name="visible">True</property> - <property name="spacing">6</property> + <property name="spacing">12</property> <child> - <widget class="GtkHBox" id="hbox2"> + <widget class="GtkImage" id="image1"> <property name="visible">True</property> - <property name="spacing">12</property> - <child> - <widget class="GtkImage" id="image1"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="yalign">1</property> - <property name="stock">gtk-dialog-authentication</property> - <property name="icon_size">6</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - <child> - <widget class="GtkVBox" id="vbox2"> - <property name="visible">True</property> - <property name="spacing">6</property> - <child> - <widget class="GtkLabel" id="primary-text"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><span size='large' weight='bold'>Import Certificates and Keys</span></property> - <property name="use_markup">True</property> - </widget> - </child> - <child> - <widget class="GtkLabel" id="secondary-text"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Secondary prompt text</property> - </widget> - <packing> - <property name="position">1</property> - </packing> - </child> - </widget> - <packing> - <property name="position">1</property> - </packing> - </child> + <property name="xalign">0</property> + <property name="yalign">1</property> + <property name="stock">gtk-dialog-authentication</property> + <property name="icon_size">6</property> </widget> <packing> <property name="expand">False</property> + <property name="fill">False</property> </packing> </child> <child> - <widget class="GtkTable" id="table1"> + <widget class="GtkVBox" id="vbox2"> <property name="visible">True</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <property name="column_spacing">12</property> - <property name="row_spacing">6</property> + <property name="spacing">6</property> <child> - <widget class="GtkEntry" id="password-entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - <child> - <widget class="GtkComboBox" id="location-combo"> - <property name="visible">True</property> - <property name="button_sensitivity">GTK_SENSITIVITY_ON</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label5"> + <widget class="GtkLabel" id="primary-text"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">Password:</property> + <property name="label" translatable="yes"><span size='large' weight='bold'>Import Certificates and Keys</span></property> + <property name="use_markup">True</property> + <property name="wrap">True</property> </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> </child> <child> - <widget class="GtkLabel" id="label4"> + <widget class="GtkLabel" id="secondary-text"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">Import Into:</property> + <property name="wrap">True</property> </widget> <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> + <property name="position">1</property> </packing> </child> </widget> <packing> - <property name="expand">False</property> <property name="position">1</property> </packing> </child> </widget> <packing> - <property name="position">1</property> + <property name="expand">False</property> </packing> </child> - <child internal-child="action_area"> - <widget class="GtkHButtonBox" id="dialog-action_area1"> + <child> + <widget class="GtkTable" id="table1"> <property name="visible">True</property> - <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">12</property> + <property name="row_spacing">6</property> + <child> + <widget class="GtkAlignment" id="password-area"> + <property name="visible">True</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkAlignment" id="slot-area"> + <property name="visible">True</property> + <child> + <widget class="GtkComboBox" id="slot-combo"> + <property name="visible">True</property> + <property name="button_sensitivity">GTK_SENSITIVITY_ON</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + </packing> + </child> <child> - <widget class="GtkButton" id="button1"> + <widget class="GtkLabel" id="password-label"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="label" translatable="yes">gtk-cancel</property> - <property name="use_stock">True</property> - <property name="response_id">0</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Password:</property> </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> </child> <child> - <widget class="GtkButton" id="button2"> + <widget class="GtkLabel" id="slot-label"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="label" translatable="yes">gtk-ok</property> - <property name="use_stock">True</property> - <property name="response_id">0</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Import Into:</property> </widget> <packing> - <property name="position">1</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> </packing> </child> </widget> <packing> <property name="expand">False</property> - <property name="pack_type">GTK_PACK_END</property> + <property name="position">1</property> </packing> </child> </widget> diff --git a/gcr/gcr-import-dialog.h b/gcr/gcr-import-dialog.h new file mode 100644 index 0000000..2465f22 --- /dev/null +++ b/gcr/gcr-import-dialog.h @@ -0,0 +1,86 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * 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. + */ + +#ifndef __GCR_IMPORT_DIALOG_H__ +#define __GCR_IMPORT_DIALOG_H__ + +#include "gcr.h" + +#include "gp11/gp11.h" + +#include <gtk/gtk.h> + +#define GCR_TYPE_IMPORT_DIALOG (_gcr_import_dialog_get_type ()) +#define GCR_IMPORT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORT_DIALOG, GcrImportDialog)) +#define GCR_IMPORT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORT_DIALOG, GcrImportDialogClass)) +#define GCR_IS_IMPORT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORT_DIALOG)) +#define GCR_IS_IMPORT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_IMPORT_DIALOG)) +#define GCR_IMPORT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_IMPORT_DIALOG, GcrImportDialogClass)) + +typedef struct _GcrImportDialog GcrImportDialog; +typedef struct _GcrImportDialogClass GcrImportDialogClass; +typedef struct _GcrImportDialogPrivate GcrImportDialogPrivate; + +struct _GcrImportDialog { + GtkDialog parent; + GcrImportDialogPrivate *pv; +}; + +struct _GcrImportDialogClass { + GtkDialogClass parent_class; +}; + +GType _gcr_import_dialog_get_type (void); + +GcrImportDialog* _gcr_import_dialog_new (void); + +gboolean _gcr_import_dialog_run (GcrImportDialog *self, + GtkWindow *parent); + +GP11Slot* _gcr_import_dialog_get_selected_slot (GcrImportDialog *self); + +void _gcr_import_dialog_set_selected_slot (GcrImportDialog *self, + GP11Slot *slot); + +void _gcr_import_dialog_show_selected_slot (GcrImportDialog *self); + +void _gcr_import_dialog_hide_selected_slot (GcrImportDialog *self); + +const gchar* _gcr_import_dialog_get_password (GcrImportDialog *self); + +void _gcr_import_dialog_set_password (GcrImportDialog *self, + const gchar *password); + +void _gcr_import_dialog_show_password (GcrImportDialog *self); + +void _gcr_import_dialog_hide_password (GcrImportDialog *self); + +const gchar* _gcr_import_dialog_get_primary_text (GcrImportDialog *self); + +void _gcr_import_dialog_set_primary_text (GcrImportDialog *self, + const gchar *text); + +const gchar* _gcr_import_dialog_get_secondary_text (GcrImportDialog *self); + +void _gcr_import_dialog_set_secondary_text (GcrImportDialog *self, + const gchar *text); + +#endif /* __GCR_IMPORT_DIALOG_H__ */ diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c index 35abba8..0fe0809 100644 --- a/gcr/gcr-importer.c +++ b/gcr/gcr-importer.c @@ -21,46 +21,555 @@ #include "config.h" +#include "gcr-import-dialog.h" #include "gcr-importer.h" +#include "gcr-internal.h" +#include "gcr-parser.h" + +#include <glib/gi18n-lib.h> enum { PROP_0, - PROP_IMPORTER + PROP_SLOT, + PROP_PARSER, + PROP_PROMPT_BEHAVIOR }; enum { - SIGNAL, + IMPORTED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GcrImporter, gcr_importer, G_TYPE_OBJECT); +struct _GcrImporterPrivate { + GP11Slot *slot; + GcrParser *parser; + GcrImporterPromptBehavior behavior; + + /* Information about last import */ + GError *error; + gboolean succeeded; + + /* State data during import */ + gboolean processing; + GCancellable *cancel; + GInputStream *input; + gboolean prompted; + gboolean async; + GByteArray *buffer; + GP11Session *session; + GQueue queue; + + /* Extra async stuff */ + GAsyncReadyCallback callback; + gpointer user_data; +}; + +/* State forward declarations */ +static void state_cancelled (GcrImporter *self, gboolean async); +static void state_complete (GcrImporter *self, gboolean async); +static void state_create_object (GcrImporter *self, gboolean async); +static void state_open_session (GcrImporter *self, gboolean async); +static void state_parse_buffer (GcrImporter *self, gboolean async); +static void state_read_buffer (GcrImporter *self, gboolean async); + +static void gcr_importer_async_result (GAsyncResultIface *iface); +G_DEFINE_TYPE_WITH_CODE (GcrImporter, gcr_importer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT, gcr_importer_async_result)); + +#define BLOCK 4096 /* ----------------------------------------------------------------------------- * INTERNAL */ +static void +cleanup_state_data (GcrImporter *self) +{ + GP11Attributes *attrs; + + if (self->pv->buffer) + g_byte_array_free (self->pv->buffer, TRUE); + self->pv->buffer = NULL; + + if (self->pv->session) + g_object_unref (self->pv->session); + self->pv->session = NULL; + + while ((attrs = g_queue_pop_head (&self->pv->queue)) != NULL) + gp11_attributes_unref (attrs); + g_assert (g_queue_is_empty (&self->pv->queue)); + + if (self->pv->input) + g_object_unref (self->pv->input); + self->pv->input = NULL; + + if (self->pv->cancel) + g_object_unref (self->pv->cancel); + self->pv->cancel = NULL; +} + +static void +cleanup_import_data (GcrImporter *self) +{ + if (self->pv->error) + g_clear_error (&self->pv->error); + self->pv->succeeded = TRUE; +} + +static void +next_state (GcrImporter *self, void (*state) (GcrImporter*, gboolean)) +{ + g_assert (GCR_IS_IMPORTER (self)); + g_assert (self->pv->processing); + g_assert (state); + + if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel)) + state = state_cancelled; + + (state) (self, self->pv->async); +} + +/* --------------------------------------------------------------------------------- + * COMPLETE + */ + +static void +state_complete (GcrImporter *self, gboolean async) +{ + if (async && self->pv->callback != NULL) + (self->pv->callback) (G_OBJECT (self), G_ASYNC_RESULT (self), self->pv->user_data); + + cleanup_state_data (self); + self->pv->processing = FALSE; +} + +static void +state_failure (GcrImporter *self, gboolean async) +{ + self->pv->succeeded = FALSE; + next_state (self, state_complete); +} + +static void +state_cancelled (GcrImporter *self, gboolean async) +{ + if (self->pv->cancel && g_cancellable_is_cancelled (self->pv->cancel)) + g_cancellable_cancel (self->pv->cancel); + if (self->pv->error) + g_error_free (self->pv->error); + self->pv->error = g_error_new_literal (GCR_DATA_ERROR, GCR_ERROR_CANCELLED, _("The operation was cancelled")); + next_state (self, state_failure); +} + +/* --------------------------------------------------------------------------------- + * CREATE OBJECTS + */ + +static void +complete_create_object (GcrImporter *self, GP11Object *object, GError *error) +{ + if (object == NULL) { + g_propagate_error (&self->pv->error, error); + next_state (self, state_failure); + + } else { + g_signal_emit (self, signals[IMPORTED], 0, object); + g_object_unref (object); + next_state (self, state_create_object); + } +} + +static void +on_create_object (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GP11Object *object = gp11_session_create_object_finish (GP11_SESSION (obj), res, &error); + complete_create_object (GCR_IMPORTER (user_data), object, error); +} + +static void +state_create_object (GcrImporter *self, gboolean async) +{ + GP11Attributes *attrs; + GP11Object *object; + GError *error = NULL; + + /* No more objects */ + if (g_queue_is_empty (&self->pv->queue)) { + next_state (self, state_complete); + + } else { + + /* Pop first one off the list */ + attrs = g_queue_pop_head (&self->pv->queue); + g_assert (attrs); + + gp11_attributes_add_ulong (attrs, CKA_TOKEN, CK_TRUE); + + if (async) { + gp11_session_create_object_async (self->pv->session, attrs, self->pv->cancel, + on_create_object, self); + } else { + object = gp11_session_create_object_full (self->pv->session, attrs, self->pv->cancel, &error); + complete_create_object (self, object, error); + } + + gp11_attributes_unref (attrs); + } +} + +/* --------------------------------------------------------------------------------- + * OPEN SESSION + */ + +static void +complete_open_session (GcrImporter *self, GP11Session *session, GError *error) +{ + if (!session) { + g_propagate_error (&self->pv->error, error); + next_state (self, state_failure); + } else { + self->pv->session = session; + next_state (self, state_create_object); + } +} + +static void +on_open_session (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GP11Session *session = gp11_slot_open_session_finish (GP11_SLOT (obj), res, &error); + complete_open_session (GCR_IMPORTER (user_data), session, error); +} + +static void +state_open_session (GcrImporter *self, gboolean async) +{ + GP11Session *session; + GError *error = NULL; + + if (!self->pv->slot) { + g_set_error (&self->pv->error, GCR_DATA_ERROR, GCR_ERROR_FAILURE, _("No location available to import to")); + next_state (self, state_failure); + + } else { + + if (async) { + gp11_slot_open_session_async (self->pv->slot, CKF_RW_SESSION, + self->pv->cancel, on_open_session, self); + } else { + session = gp11_slot_open_session_full (self->pv->slot, CKF_RW_SESSION, + self->pv->cancel, &error); + complete_open_session (self, session, error); + } + } +} + +/* --------------------------------------------------------------------------------- + * IMPORT PROMPT + */ + +static void +complete_import_prompt (GcrImporter *self, GcrImportDialog *dialog, gint response) +{ + GP11Slot *slot; + + gtk_widget_hide (GTK_WIDGET (dialog)); + self->pv->prompted = TRUE; + + /* No dialog or dialog completed */ + if (response == GTK_RESPONSE_OK) { + + slot = _gcr_import_dialog_get_selected_slot (dialog); + gcr_importer_set_slot (self, slot); + next_state (self, state_open_session); + + /* The dialog was cancelled or closed */ + } else { + next_state (self, state_cancelled); + } +} + +static void +on_prompt_response (GtkDialog *dialog, gint response, gpointer user_data) +{ + complete_import_prompt (GCR_IMPORTER (user_data), GCR_IMPORT_DIALOG (dialog), response); + g_object_unref (dialog); +} + +static void +state_import_prompt (GcrImporter *self, gboolean async) +{ + GcrImportDialog *dialog; + gboolean prompt; + gint response; + + g_assert (GCR_IS_IMPORTER (self)); + + /* No need to prompt */ + if (self->pv->prompted == TRUE) + prompt = FALSE; + else if (self->pv->behavior == GCR_IMPORTER_PROMPT_ALWAYS) + prompt = TRUE; + else if (self->pv->behavior == GCR_IMPORTER_PROMPT_NEVER) + prompt = FALSE; + else + prompt = self->pv->slot ? FALSE : TRUE; + + if (prompt == FALSE) { + next_state (self, state_open_session); + + } else { + + dialog = _gcr_import_dialog_new (); + + _gcr_import_dialog_set_primary_text (dialog, _("Import Certificates/Keys")); + _gcr_import_dialog_hide_password (dialog); + + if (self->pv->slot) { + _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot); + _gcr_import_dialog_hide_selected_slot (dialog); + } else { + _gcr_import_dialog_set_secondary_text (dialog, _("Choose a location to store the imported certificates/keys.")); + } + + /* Prompt without blocking main loop */ + if (async) { + g_signal_connect (dialog, "response", G_CALLBACK (on_prompt_response), self); + gtk_widget_show (GTK_WIDGET (dialog)); + + /* Block mainloop */ + } else { + response = gtk_dialog_run (GTK_DIALOG (dialog)); + complete_import_prompt (self, dialog, response); + g_object_unref (dialog); + } + } +} + +/* --------------------------------------------------------------------------------- + * PARSING + */ + +static const gchar* +prepare_auth_primary (CK_OBJECT_CLASS klass) +{ + if (klass == CKO_PRIVATE_KEY) + return _("Enter password to unlock the private key"); + else if (klass == CKO_CERTIFICATE) + return _("Enter password to unlock the certificate"); + else + return _("Enter password to unlock"); +} + +static gchar* +prepare_auth_secondary (CK_OBJECT_CLASS klass, const gchar *label) +{ + if (label == NULL) { + if (klass == CKO_PRIVATE_KEY) + return g_strdup (_("In order to import the private key, it must be unlocked")); + else if (klass == CKO_CERTIFICATE) + return g_strdup (_("In order to import the certificate, it must be unlocked")); + else + return g_strdup (_("In order to import the data, it must be unlocked")); + } else { + if (klass == CKO_PRIVATE_KEY) + return g_strdup_printf (_("In order to import the private key '%s', it must be unlocked"), label); + else if (klass == CKO_CERTIFICATE) + return g_strdup_printf (_("In order to import the certificate '%s', it must be unlocked"), label); + else + return g_strdup_printf (_("In order to import '%s', it must be unlocked"), label); + } +} + +static void +on_parser_parsed (GcrParser *parser, GcrImporter *self) +{ + GP11Attributes *attrs; + + g_return_if_fail (GCR_IS_PARSER (parser)); + g_return_if_fail (GCR_IS_IMPORTER (self)); + + attrs = gcr_parser_get_parsed_attributes (parser); + g_return_if_fail (attrs); + g_queue_push_tail (&self->pv->queue, gp11_attributes_ref (attrs)); +} + +static gboolean +on_parser_authenticate (GcrParser *parser, gint count, GcrImporter *self) +{ + GcrImportDialog *dialog; + GP11Attributes *attrs; + const gchar *password; + gchar *text, *label; + GP11Slot *slot; + gulong klass; + + dialog = _gcr_import_dialog_new (); + + if (self->pv->slot) + _gcr_import_dialog_set_selected_slot (dialog, self->pv->slot); + + /* Figure out the text for the dialog */ + attrs = gcr_parser_get_parsed_attributes (parser); + g_return_val_if_fail (attrs, FALSE); + + if (!gp11_attributes_find_ulong (attrs, CKA_CLASS, &klass)) + klass = (gulong)-1; + if (!gp11_attributes_find_string (attrs, CKA_LABEL, &label)) + label = NULL; + + text = prepare_auth_secondary (klass, label); + _gcr_import_dialog_set_primary_text (dialog, prepare_auth_primary (klass)); + _gcr_import_dialog_set_secondary_text (dialog, text); + g_free (label); + g_free (text); + + if (!_gcr_import_dialog_run (dialog, NULL)) + return FALSE; + + slot = _gcr_import_dialog_get_selected_slot (dialog); + gcr_importer_set_slot (self, slot); + + password = _gcr_import_dialog_get_password (dialog); + gcr_parser_add_password (parser, password); + + g_object_unref (dialog); + self->pv->prompted = TRUE; + return TRUE; +} + +static void +state_parse_buffer (GcrImporter *self, gboolean async) +{ + GError *error = NULL; + GcrParser *parser; + gulong parsed_conn; + gulong auth_conn; + gboolean ret; + + g_assert (GCR_IS_IMPORTER (self)); + g_assert (self->pv->buffer); + + parser = gcr_importer_get_parser (self); + g_object_ref (parser); + + /* Listen in to the parser */ + parsed_conn = g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_parsed), self); + auth_conn = g_signal_connect (parser, "authenticate", G_CALLBACK (on_parser_authenticate), self); + + ret = gcr_parser_parse_data (parser, self->pv->buffer->data, self->pv->buffer->len, &error); + + /* An optimization to free data early as possible */ + g_byte_array_free (self->pv->buffer, TRUE); + self->pv->buffer = NULL; + + g_signal_handler_disconnect (parser, parsed_conn); + g_signal_handler_disconnect (parser, auth_conn); + g_object_unref (parser); + + if (ret == TRUE) { + next_state (self, state_import_prompt); + } else { + g_propagate_error (&self->pv->error, error); + next_state (self, state_failure); + } +} + +/* --------------------------------------------------------------------------------- + * BUFFER READING + */ + +static void +complete_read_buffer (GcrImporter *self, gssize count, GError *error) +{ + g_assert (GCR_IS_IMPORTER (self)); + g_assert (self->pv->buffer); + + /* A failure */ + if (count == -1) { + g_propagate_error (&self->pv->error, error); + next_state (self, state_failure); + } else { + + g_return_if_fail (count >= 0 && count <= BLOCK); + g_byte_array_set_size (self->pv->buffer, self->pv->buffer->len - (BLOCK - count)); + + /* Finished reading */ + if (count == 0) { + + /* Optimization, unref input early */ + g_object_unref (self->pv->input); + self->pv->input = NULL; + + next_state (self, state_parse_buffer); + + /* Read the next block */ + } else { + next_state (self, state_read_buffer); + } + } + +} + +static void +on_read_buffer (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + gssize count; + + count = g_input_stream_read_finish (G_INPUT_STREAM (obj), res, &error); + complete_read_buffer (user_data, count, error); +} + +static void +state_read_buffer (GcrImporter *self, gboolean async) +{ + GError *error = NULL; + gssize count; + gsize at; + + g_assert (GCR_IS_IMPORTER (self)); + g_assert (G_IS_INPUT_STREAM (self->pv->input)); + + if (!self->pv->buffer) + self->pv->buffer = g_byte_array_sized_new (BLOCK); + + at = self->pv->buffer->len; + g_byte_array_set_size (self->pv->buffer, at + BLOCK); + + if (async) { + g_input_stream_read_async (self->pv->input, self->pv->buffer->data + at, + BLOCK, G_PRIORITY_DEFAULT, self->pv->cancel, + on_read_buffer, self); + } else { + count = g_input_stream_read (self->pv->input, self->pv->buffer->data + at, + BLOCK, self->pv->cancel, &error); + complete_read_buffer (self, count, error); + } +} + /* ----------------------------------------------------------------------------- * OBJECT */ - static GObject* gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props) { GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props)); g_return_val_if_fail (self, NULL); - - return G_OBJECT (self); } static void gcr_importer_init (GcrImporter *self) { - + self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_IMPORTER, GcrImporterPrivate); + self->pv->behavior = GCR_IMPORTER_PROMPT_NEEDED; + g_queue_init (&self->pv->queue); } static void @@ -68,6 +577,17 @@ gcr_importer_dispose (GObject *obj) { GcrImporter *self = GCR_IMPORTER (obj); + cleanup_state_data (self); + cleanup_import_data (self); + + if (self->pv->parser) + g_object_unref (self->pv->parser); + self->pv->parser = NULL; + + if (self->pv->slot) + g_object_unref (self->pv->slot); + self->pv->slot = NULL; + G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj); } @@ -76,6 +596,9 @@ gcr_importer_finalize (GObject *obj) { GcrImporter *self = GCR_IMPORTER (obj); + g_assert (!self->pv->parser); + g_assert (!self->pv->slot); + G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj); } @@ -86,7 +609,14 @@ gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value, GcrImporter *self = GCR_IMPORTER (obj); switch (prop_id) { - case PROP_IMPORTER: + case PROP_PARSER: + gcr_importer_set_parser (self, g_value_get_object (value)); + break; + case PROP_SLOT: + gcr_importer_set_slot (self, g_value_get_object (value)); + break; + case PROP_PROMPT_BEHAVIOR: + gcr_importer_set_prompt_behavior (self, (GcrImporterPromptBehavior)g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); @@ -101,7 +631,14 @@ gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value, GcrImporter *self = GCR_IMPORTER (obj); switch (prop_id) { - case PROP_IMPORTER: + case PROP_PARSER: + g_value_set_object (value, gcr_importer_get_parser (self)); + break; + case PROP_SLOT: + g_value_set_object (value, gcr_importer_get_slot (self)); + break; + case PROP_PROMPT_BEHAVIOR: + g_value_set_int (value, gcr_importer_get_prompt_behavior (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); @@ -120,17 +657,49 @@ gcr_importer_class_init (GcrImporterClass *klass) gobject_class->set_property = gcr_importer_set_property; gobject_class->get_property = gcr_importer_get_property; - g_object_class_install_property (gobject_class, PROP_IMPORTER, - g_param_spec_pointer ("importer", "Importer", "Importer.", G_PARAM_READWRITE)); + g_type_class_add_private (gobject_class, sizeof (GcrImporterPrivate)); + + g_object_class_install_property (gobject_class, PROP_PARSER, + g_param_spec_object ("parser", "Parser", "Parser used to parse imported data", + GCR_TYPE_PARSER, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_PARSER, + g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into", + GP11_TYPE_SLOT, G_PARAM_READWRITE)); - signals[SIGNAL] = g_signal_new ("signal", GCR_TYPE_IMPORTER, - G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, signal), + g_object_class_install_property (gobject_class, PROP_PROMPT_BEHAVIOR, + g_param_spec_int ("prompt-behavior", "Prompt Behavior", "Import Prompt Behavior", + 0, G_MAXINT, GCR_IMPORTER_PROMPT_NEEDED, G_PARAM_READWRITE)); + + signals[IMPORTED] = g_signal_new ("imported", GCR_TYPE_IMPORTER, + G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, imported), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 0); + G_TYPE_NONE, 1, GP11_TYPE_OBJECT); _gcr_initialize (); } +static gpointer +gcr_importer_real_get_user_data (GAsyncResult *base) +{ + g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL); + return GCR_IMPORTER (base)->pv->user_data; +} + +static GObject* +gcr_importer_real_get_source_object (GAsyncResult *base) +{ + g_return_val_if_fail (GCR_IS_IMPORTER (base), NULL); + return G_OBJECT (base); +} + +static void +gcr_importer_async_result (GAsyncResultIface *iface) +{ + iface->get_source_object = gcr_importer_real_get_source_object; + iface->get_user_data = gcr_importer_real_get_user_data; +} + /* ----------------------------------------------------------------------------- * PUBLIC */ @@ -141,73 +710,133 @@ gcr_importer_new (void) return g_object_new (GCR_TYPE_IMPORTER, NULL); } -gboolean -gcr_importer_import_data (GcrImporter *self, const guchar *data, gsize n_data, - GError *err) +GcrParser* +gcr_importer_get_parser (GcrImporter *self) { - GckParser *parser; - gulong parsed_conn; - gulong auth_conn; - gboolean ret; - - g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE); - g_return_val_if_fail (data || !n_data, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL); + if (!self->pv->parser) + self->pv->parser = gcr_parser_new (); + return self->pv->parser; +} +void +gcr_importer_set_parser (GcrImporter *self, GcrParser *parser) +{ + g_return_if_fail (GCR_IS_IMPORTER (self)); + + if (parser) + g_object_ref (parser); + if (self->pv->parser) + g_object_unref (self->pv->parser); + self->pv->parser = parser; + g_object_notify (G_OBJECT (self), "parser"); +} - xxxx; +GP11Slot* +gcr_importer_get_slot (GcrImporter *self) +{ + g_return_val_if_fail (GCR_IS_IMPORTER (self), NULL); + return self->pv->slot; +} +void +gcr_importer_set_slot (GcrImporter *self, GP11Slot *slot) +{ + g_return_if_fail (GCR_IS_IMPORTER (self)); + + if (slot) + g_object_ref (slot); + if (self->pv->slot) + g_object_unref (self->pv->slot); + self->pv->slot = slot; + g_object_notify (G_OBJECT (self), "slot"); +} - /* - * Parse to see if it's something that needs a password - * if we can't prompt, - * return an error - * Possibly prompt, if password needed, with all information necessary - * - */ +GcrImporterPromptBehavior +gcr_importer_get_prompt_behavior (GcrImporter *self) +{ + g_return_val_if_fail (GCR_IS_IMPORTER (self), GCR_IMPORTER_PROMPT_NEEDED); + return self->pv->behavior; +} +void +gcr_importer_set_prompt_behavior (GcrImporter *self, GcrImporterPromptBehavior behavior) +{ + g_return_if_fail (GCR_IMPORTER (self)); + self->pv->behavior = behavior; + g_object_notify (G_OBJECT (self), "prompt-behavior"); +} - g_object_ref (self); +gboolean +gcr_importer_import (GcrImporter *self, GInputStream *input, + GCancellable *cancel, GError **error) +{ + g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE); + g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (!self->pv->processing, FALSE); - parser = gcr_importer_get_parser (self); + cleanup_import_data (self); - /* Listen in to the parser */ - g_object_ref (parser); - parsed_conn = g_signal_connect (parser, "parsed-item", G_CALLBACK (parser_parsed_item), self); - auth_conn = g_signal_connect (parser, "authenticate", G_CALLBACK (parser_authenticate), self); + self->pv->input = g_object_ref (input); + if (cancel) + self->pv->cancel = g_object_ref (cancel); + self->pv->processing = TRUE; + self->pv->async = FALSE; - /* Feed the parser the data */ - ret = gcr_parser_parse_data (parser, data, n_data, err); + next_state (self, state_read_buffer); - /* Now we should have all the data ready, check if we should prompt... */ - /* Import data one by one into module */ + g_assert (!self->pv->processing); + g_assert (!self->pv->input); + g_assert (!self->pv->cancel); - g_signal_handler_disconnect (parser, parsed_conn); - g_signal_handler_disconnect (parser, auth_conn); - g_object_unref (parser); + if (!self->pv->succeeded) { + g_propagate_error (error, self->pv->error); + self->pv->error = NULL; + return FALSE; + } - g_object_unref (self); + return TRUE; +} - return ret; +void +gcr_importer_import_async (GcrImporter *self, GInputStream *input, GCancellable *cancel, + GAsyncReadyCallback callback, gpointer user_data) +{ + g_return_if_fail (GCR_IS_IMPORTER (self)); + g_return_if_fail (G_IS_INPUT_STREAM (input)); + g_return_if_fail (!self->pv->processing); + + cleanup_import_data (self); + + self->pv->input = g_object_ref (input); + if (cancel) + self->pv->cancel = g_object_ref (cancel); + self->pv->processing = TRUE; + self->pv->async = TRUE; + self->pv->callback = callback; + self->pv->user_data = user_data; + + next_state (self, state_read_buffer); + g_assert (self->pv->processing); } gboolean -gcr_importer_import_file (GcrImporter *self, const gchar *filename, - GError *err) +gcr_importer_import_finish (GcrImporter *self, GAsyncResult *res, GError **error) { - gboolean ret; - gchar *data; - gsize n_data; - g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE); - g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail (GCR_IMPORTER (res) == self, FALSE); g_return_val_if_fail (!error || !*error, FALSE); + g_return_val_if_fail (!self->pv->processing, FALSE); - if (!g_file_get_contents (filename, &data, &n_data, err)) - return FALSE; + g_assert (!self->pv->input); + g_assert (!self->pv->cancel); - ret = gcr_importer_import_data (self, (const guchar*)data, n_data, error); - g_free (data); + if (!self->pv->succeeded) { + g_propagate_error (error, self->pv->error); + self->pv->error = NULL; + return FALSE; + } - return ret; + return TRUE; } diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h index dac1d51..ed366f3 100644 --- a/gcr/gcr-importer.h +++ b/gcr/gcr-importer.h @@ -22,8 +22,17 @@ #ifndef __GCR_IMPORTER_H__ #define __GCR_IMPORTER_H__ +#include "gcr.h" +#include "gcr-parser.h" + #include <glib-object.h> +typedef enum { + GCR_IMPORTER_PROMPT_NEEDED, + GCR_IMPORTER_PROMPT_ALWAYS, + GCR_IMPORTER_PROMPT_NEVER +} GcrImporterPromptBehavior; + #define GCR_TYPE_IMPORTER (gcr_importer_get_type ()) #define GCR_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter)) #define GCR_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORTER, GcrImporterClass)) @@ -33,49 +42,53 @@ typedef struct _GcrImporter GcrImporter; typedef struct _GcrImporterClass GcrImporterClass; +typedef struct _GcrImporterPrivate GcrImporterPrivate; struct _GcrImporter { GObject parent; + GcrImporterPrivate *pv; }; struct _GcrImporterClass { GObjectClass parent_class; - /* signals --------------------------------------------------------- */ + /* signals */ - void (*signal) (GcrImporter *self, GkrImportedItem *item); + void (*imported) (GcrImporter *self, GP11Object *object); }; -GType gcr_importer_get_type (void); +GType gcr_importer_get_type (void); -GcrImporter* gcr_importer_new (void); +GcrImporter* gcr_importer_new (void); -GcrImporter* gcr_importer_new_for_module (GP11Module *module); +GcrParser* gcr_importer_get_parser (GcrImporter *self); -GcrImporter* gcr_importer_new_for_module_funcs (gpointer pkcs11_funcs); +void gcr_importer_set_parser (GcrImporter *self, + GcrParser *parser); -void gcr_importer_set_slot (GcrImporter *self, - GP11Slot *slot); +struct _GP11Slot* gcr_importer_get_slot (GcrImporter *self); -void gcr_importer_set_slot_id (GcrImporter *self, - gulong slot_id); +void gcr_importer_set_slot (GcrImporter *self, + struct _GP11Slot *slot); -void gcr_importer_set_parser (GcrImporter *self, - GcrParser *parser); +GcrImporterPromptBehavior gcr_importer_get_prompt_behavior (GcrImporter *self); -void gcr_importer_set_window (GcrImporter *self, - GtkWindow *window); +void gcr_importer_set_prompt_behavior (GcrImporter *self, + GcrImporterPromptBehavior behavior); -void gcr_importer_set_prompt_behavior (GcrImporter *self, - GcrImporterPromptBehavior behavior); +gboolean gcr_importer_import (GcrImporter *self, + GInputStream *input, + GCancellable *cancel, + GError **error); -gboolean gcr_importer_import_data (GcrImporter *self, - const guchar *data, - gsize n_data, - GError *error); +void gcr_importer_import_async (GcrImporter *self, + GInputStream *input, + GCancellable *cancel, + GAsyncReadyCallback callback, + gpointer user_data); -gboolean gcr_importer_import_file (GcrImporter *self, - const gchar *filename, - GError *error); +gboolean gcr_importer_import_finish (GcrImporter *self, + GAsyncResult *res, + GError **error); #endif /* __GCR_IMPORTER_H__ */ diff --git a/gcr/gcr-internal.h b/gcr/gcr-internal.h index daad990..a8a4651 100644 --- a/gcr/gcr-internal.h +++ b/gcr/gcr-internal.h @@ -1,8 +1,14 @@ #ifndef GCR_INTERNAL_H_ #define GCR_INTERNAL_H_ +#include "gcr.h" + +#include "gp11/gp11.h" + #include <glib.h> -void _gcr_initialize (void); +void _gcr_initialize (void); + +GList* _gcr_get_pkcs11_modules (void); #endif /* GCR_INTERNAL_H_ */ diff --git a/gcr/gcr-internal.c b/gcr/gcr-library.c index 8daddee..41356b5 100644 --- a/gcr/gcr-internal.c +++ b/gcr/gcr-library.c @@ -21,12 +21,95 @@ #include "config.h" +#include "gcr.h" +#include "gcr-types.h" #include "gcr-internal.h" #include "egg/egg-secure-memory.h" #include <gcrypt.h> +static GList *all_modules = NULL; + +GQuark +gcr_data_error_get_domain (void) +{ + static GQuark domain = 0; + if (domain == 0) + domain = g_quark_from_static_string ("gcr-parser-error"); + return domain; +} + +/* ----------------------------------------------------------------------------- + * MEMORY + */ + +static gboolean do_warning = TRUE; +#define WARNING "couldn't allocate secure memory to keep passwords " \ + "and or keys from being written to the disk" + +#define ABORTMSG "The GNOME_KEYRING_PARANOID environment variable was set. " \ + "Exiting..." + +static G_LOCK_DEFINE (memory_lock); + +/* + * These are called from egg-secure-memory.c to provide appropriate + * locking for memory between threads + */ + +void +egg_memory_lock (void) +{ + G_LOCK (memory_lock); +} + +void +egg_memory_unlock (void) +{ + G_UNLOCK (memory_lock); +} + +void* +egg_memory_fallback (void *p, unsigned long sz) +{ + const gchar *env; + + /* We were asked to free memory */ + if (!sz) { + g_free (p); + return NULL; + } + + /* We were asked to allocate */ + if (!p) { + if (do_warning) { + g_message (WARNING); + do_warning = FALSE; + } + + env = g_getenv ("GNOME_KEYRING_PARANOID"); + if (env && *env) + g_error (ABORTMSG); + + return g_malloc0 (sz); + } + + /* + * Reallocation is a bit of a gray area, as we can be asked + * by external libraries (like libgcrypt) to reallocate a + * non-secure block into secure memory. We cannot satisfy + * this request (as we don't know the size of the original + * block) so we just try our best here. + */ + + return g_realloc (p, sz); +} + +/* ------------------------------------------------------------------------------ + * GCRYPT HOOKS + */ + static void log_handler (gpointer unused, int unknown, const gchar *msg, va_list va) { @@ -88,10 +171,12 @@ static struct gcry_thread_cbs glib_thread_cbs = { void _gcr_initialize (void) { - static gsize gcrypt_initialized = FALSE; + static volatile gsize gcr_initialized = 0; + GP11Module *module; + GError *error = NULL; unsigned seed; - if (g_once_init_enter (&gcrypt_initialized)) { + if (g_once_init_enter (&gcr_initialized)) { /* Only initialize libgcrypt if it hasn't already been initialized */ if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) { @@ -111,6 +196,20 @@ _gcr_initialize (void) gcry_create_nonce (&seed, sizeof (seed)); srand (seed); - g_once_init_leave (&gcrypt_initialized, 1); + /* TODO: This needs reworking for multiple modules */ + module = gp11_module_initialize (PKCS11_MODULE_PATH, NULL, &error); + if (module) + all_modules = g_list_prepend (all_modules, module); + else + g_warning ("couldn't initialize PKCS#11 module: %s", + error && error->message ? error->message : ""); + + g_once_init_leave (&gcr_initialized, 1); } } + +GList* +_gcr_get_pkcs11_modules (void) +{ + return all_modules; +} diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c index 7348788..505f9f9 100644 --- a/gcr/gcr-parser.c +++ b/gcr/gcr-parser.c @@ -263,7 +263,7 @@ enum_next_password (GcrParser *self, PasswordState *state, const gchar **passwor ++state->ask_state; if (!result) - return GCR_PARSE_CANCELLED; + return GCR_ERROR_CANCELLED; /* Return any passwords added */ if (state->seen < self->pv->passwords->len) { @@ -273,7 +273,7 @@ enum_next_password (GcrParser *self, PasswordState *state, const gchar **passwor return SUCCESS; } - return GCR_PARSE_LOCKED; + return GCR_ERROR_LOCKED; } static void @@ -293,7 +293,7 @@ parsed_fire (GcrParser *self) static gint parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data) { - gint res = GCR_PARSE_UNRECOGNIZED; + gint res = GCR_ERROR_UNRECOGNIZED; ASN1_TYPE asn = ASN1_TYPE_EMPTY; guint version; @@ -303,14 +303,14 @@ parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data) parsed_clear (self, CKO_PRIVATE_KEY); parsed_ulong (self, CKA_KEY_TYPE, CKK_RSA); - res = GCR_PARSE_FAILURE; + res = GCR_ERROR_FAILURE; if (!egg_asn1_read_uint (asn, "version", &version)) goto done; /* We only support simple version */ if (version != 0) { - res = GCR_PARSE_UNRECOGNIZED; + res = GCR_ERROR_UNRECOGNIZED; g_message ("unsupported version of RSA key: %u", version); goto done; } @@ -330,7 +330,7 @@ done: if (asn) asn1_delete_structure (&asn); - if (res == GCR_PARSE_FAILURE) + if (res == GCR_ERROR_FAILURE) g_message ("invalid RSA key"); return res; @@ -343,7 +343,7 @@ done: static gint parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data) { - gint ret = GCR_PARSE_UNRECOGNIZED; + gint ret = GCR_ERROR_UNRECOGNIZED; int res; ASN1_TYPE asn; @@ -353,7 +353,7 @@ parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data) parsed_clear (self, CKO_PRIVATE_KEY); parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA); - res = GCR_PARSE_FAILURE; + res = GCR_ERROR_FAILURE; if (!parsed_asn1_attribute (self, asn, data, n_data, "p", CKA_PRIME) || !parsed_asn1_attribute (self, asn, data, n_data, "q", CKA_SUBPRIME) || @@ -368,7 +368,7 @@ done: if (asn) asn1_delete_structure (&asn); - if (ret == GCR_PARSE_FAILURE) + if (ret == GCR_ERROR_FAILURE) g_message ("invalid DSA key"); return ret; @@ -378,7 +378,7 @@ static gint parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n_keydata, const guchar *params, gsize n_params) { - gint ret = GCR_PARSE_UNRECOGNIZED; + gint ret = GCR_ERROR_UNRECOGNIZED; int res; ASN1_TYPE asn_params = ASN1_TYPE_EMPTY; ASN1_TYPE asn_key = ASN1_TYPE_EMPTY; @@ -390,7 +390,7 @@ parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n parsed_clear (self, CKO_PRIVATE_KEY); parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA); - res = GCR_PARSE_FAILURE; + res = GCR_ERROR_FAILURE; if (!parsed_asn1_attribute (self, asn_params, params, n_params, "p", CKA_PRIME) || !parsed_asn1_attribute (self, asn_params, params, n_params, "q", CKA_SUBPRIME) || @@ -407,7 +407,7 @@ done: if (asn_params) asn1_delete_structure (&asn_params); - if (ret == GCR_PARSE_FAILURE) + if (ret == GCR_ERROR_FAILURE) g_message ("invalid DSA key"); return ret; @@ -423,7 +423,7 @@ parse_der_private_key (GcrParser *self, const guchar *data, gsize n_data) gint res; res = parse_der_private_key_rsa (self, data, n_data); - if (res == GCR_PARSE_UNRECOGNIZED) + if (res == GCR_ERROR_UNRECOGNIZED) res = parse_der_private_key_dsa (self, data, n_data); return res; @@ -445,13 +445,13 @@ parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data) const guchar *params; gsize n_params; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-8-PrivateKeyInfo", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; key_type = GP11_INVALID; key_algo = egg_asn1_read_oid (asn, "privateKeyAlgorithm.algorithm"); @@ -463,7 +463,7 @@ parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data) key_type = CKK_DSA; if (key_type == GP11_INVALID) { - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; goto done; } @@ -487,17 +487,17 @@ done: ret = parse_der_private_key_dsa (self, keydata, n_keydata); /* Otherwise try the two part format that everyone seems to like */ - if (ret == GCR_PARSE_UNRECOGNIZED && params && n_params) + if (ret == GCR_ERROR_UNRECOGNIZED && params && n_params) ret = parse_der_private_key_dsa_parts (self, keydata, n_keydata, params, n_params); break; default: g_message ("invalid or unsupported key type in PKCS#8 key"); - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; break; }; - } else if (ret == GCR_PARSE_FAILURE) { + } else if (ret == GCR_ERROR_FAILURE) { g_message ("invalid PKCS#8 key"); } @@ -521,13 +521,13 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data) const gchar *password; gint l; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-8-EncryptedPrivateKeyInfo", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; /* Figure out the type of encryption */ scheme = egg_asn1_read_oid (asn, "encryptionAlgorithm.algorithm"); @@ -576,7 +576,7 @@ parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data) egg_secure_free (crypted); crypted = NULL; - if (r != GCR_PARSE_UNRECOGNIZED) { + if (r != GCR_ERROR_UNRECOGNIZED) { ret = r; break; } @@ -600,7 +600,7 @@ parse_der_pkcs8 (GcrParser *self, const guchar *data, gsize n_data) gint ret; ret = parse_der_pkcs8_plain (self, data, n_data); - if (ret == GCR_PARSE_UNRECOGNIZED) + if (ret == GCR_ERROR_UNRECOGNIZED) ret = parse_der_pkcs8_encrypted (self, data, n_data); return ret; @@ -618,7 +618,7 @@ parse_der_certificate (GcrParser *self, const guchar *data, gsize n_data) asn = egg_asn1_decode ("PKIX1.Certificate", data, n_data); if (asn == NULL) - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; parsed_clear (self, CKO_CERTIFICATE); parsed_ulong (self, CKA_CERTIFICATE_TYPE, CKC_X_509); @@ -651,13 +651,13 @@ handle_pkcs7_signed_data (GcrParser *self, const guchar *data, gsize n_data) gsize n_certificate; int i; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-7-SignedData", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; for (i = 0; TRUE; ++i) { @@ -694,13 +694,13 @@ parse_der_pkcs7 (GcrParser *self, const guchar *data, gsize n_data) gsize n_content; GQuark oid; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-7-ContentInfo", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; oid = egg_asn1_read_oid (asn, "contentType"); if (!oid) @@ -736,13 +736,13 @@ handle_pkcs12_cert_bag (GcrParser *self, const guchar *data, gsize n_data) gsize n_certificate; gint ret; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-12-CertBag", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; certificate = egg_asn1_read_content (asn, data, n_data, "certValue", &n_certificate); if (!certificate) @@ -775,13 +775,13 @@ handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data) const guchar *element; gsize n_element; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-12-SafeContents", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; /* Get the number of elements in this bag */ res = asn1_number_of_elements (asn, "", &count); @@ -822,10 +822,10 @@ handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data) /* TODO: OID_PKCS12_BAG_CRL */ } else { - r = GCR_PARSE_UNRECOGNIZED; + r = GCR_ERROR_UNRECOGNIZED; } - if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) { + if (r == GCR_ERROR_FAILURE || r == GCR_ERROR_CANCELLED) { ret = r; goto done; } @@ -855,13 +855,13 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data) gint ret, r; gint l; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-7-EncryptedData", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; /* Check the encryption schema OID */ scheme = egg_asn1_read_oid (asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm"); @@ -887,7 +887,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data) /* Parse the encryption stuff into a cipher. */ if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih)) { - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; goto done; } @@ -915,7 +915,7 @@ handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data) egg_secure_free (crypted); crypted = NULL; - if (r != GCR_PARSE_UNRECOGNIZED) { + if (r != GCR_ERROR_UNRECOGNIZED) { ret = r; break; } @@ -944,13 +944,13 @@ handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data) GQuark oid; guint i; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-12-AuthenticatedSafe", data, n_data); if (!asn) goto done; - ret = GCR_PARSE_FAILURE; + ret = GCR_ERROR_FAILURE; /* * Inside each PKCS12 safe there are multiple bags. @@ -992,10 +992,10 @@ handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data) /* Hmmmm, not sure what this is */ } else { g_warning ("unrecognized type of safe content in pkcs12: %s", g_quark_to_string (oid)); - r = GCR_PARSE_UNRECOGNIZED; + r = GCR_ERROR_UNRECOGNIZED; } - if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) { + if (r == GCR_ERROR_FAILURE || r == GCR_ERROR_CANCELLED) { ret = r; goto done; } @@ -1019,7 +1019,7 @@ parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data) gsize n_content; GQuark oid; - ret = GCR_PARSE_UNRECOGNIZED; + ret = GCR_ERROR_UNRECOGNIZED; asn = egg_asn1_decode ("PKIX1.pkcs-12-PFX", data, n_data); if (!asn) @@ -1091,14 +1091,14 @@ handle_plain_pem (GcrParser *self, GQuark type, gint subformat, format_id = GCR_FORMAT_DER_PKCS12; else - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; if (subformat != 0 && subformat != format_id) - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; format = parser_format_lookup (format_id); if (format == NULL) - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; return (format->function) (self, data, n_data); } @@ -1143,7 +1143,7 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat, val = g_hash_table_lookup (headers, "DEK-Info"); if (!val) { g_message ("missing encryption header"); - return GCR_PARSE_FAILURE; + return GCR_ERROR_FAILURE; } /* Fill in information necessary for prompting */ @@ -1162,7 +1162,7 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat, ret = egg_openssl_decrypt_block (val, password, -1, data, n_data, &decrypted, &n_decrypted); if (!ret) - return GCR_PARSE_FAILURE; + return GCR_ERROR_FAILURE; g_assert (decrypted); @@ -1176,11 +1176,11 @@ handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat, egg_secure_free (decrypted); /* Unrecognized is a bad password */ - if (res != GCR_PARSE_UNRECOGNIZED) + if (res != GCR_ERROR_UNRECOGNIZED) return res; } - return GCR_PARSE_FAILURE; + return GCR_ERROR_FAILURE; } typedef struct { @@ -1194,12 +1194,12 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data, GHashTable *headers, gpointer user_data) { HandlePemArgs *args = (HandlePemArgs*)user_data; - gint res = GCR_PARSE_FAILURE; + gint res = GCR_ERROR_FAILURE; gboolean encrypted = FALSE; const gchar *val; /* Something already failed to parse */ - if (args->result == GCR_PARSE_FAILURE) + if (args->result == GCR_ERROR_FAILURE) return; /* See if it's encrypted PEM all openssl like*/ @@ -1216,8 +1216,8 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data, res = handle_plain_pem (args->parser, type, args->subformat, data, n_data); - if (res != GCR_PARSE_UNRECOGNIZED) { - if (args->result == GCR_PARSE_UNRECOGNIZED) + if (res != GCR_ERROR_UNRECOGNIZED) { + if (args->result == GCR_ERROR_UNRECOGNIZED) args->result = res; else if (res > args->result) args->result = res; @@ -1227,16 +1227,16 @@ handle_pem_data (GQuark type, const guchar *data, gsize n_data, static gint handle_pem_format (GcrParser *self, gint subformat, const guchar *data, gsize n_data) { - HandlePemArgs ctx = { self, GCR_PARSE_UNRECOGNIZED, subformat }; + HandlePemArgs ctx = { self, GCR_ERROR_UNRECOGNIZED, subformat }; guint found; if (n_data == 0) - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; found = egg_openssl_pem_parse (data, n_data, handle_pem_data, &ctx); if (found == 0) - return GCR_PARSE_UNRECOGNIZED; + return GCR_ERROR_UNRECOGNIZED; return ctx.result; } @@ -1375,7 +1375,7 @@ parser_format_foreach (gpointer key, gpointer value, gpointer data) g_assert (GCR_IS_PARSER (args->parser)); result = (format->function) (args->parser, args->data, args->n_data); - if (result != GCR_PARSE_UNRECOGNIZED) { + if (result != GCR_ERROR_UNRECOGNIZED) { args->result = result; return TRUE; } @@ -1534,15 +1534,6 @@ gcr_parser_new (void) return g_object_new (GCR_TYPE_PARSER, NULL); } -GQuark -gcr_parser_get_error_domain (void) -{ - static GQuark domain = 0; - if (domain == 0) - domain = g_quark_from_static_string ("gcr-parser-error"); - return domain; -} - void gcr_parser_add_password (GcrParser *self, const gchar *password) { @@ -1554,7 +1545,7 @@ gboolean gcr_parser_parse_data (GcrParser *self, const guchar *data, gsize n_data, GError **err) { - ForeachArgs args = { self, data, n_data, GCR_PARSE_UNRECOGNIZED }; + ForeachArgs args = { self, data, n_data, GCR_ERROR_UNRECOGNIZED }; const gchar *message; gint i; @@ -1578,16 +1569,16 @@ gcr_parser_parse_data (GcrParser *self, const guchar *data, switch (args.result) { case SUCCESS: return TRUE; - case GCR_PARSE_CANCELLED: + case GCR_ERROR_CANCELLED: message = _("The operation was cancelled"); break; - case GCR_PARSE_UNRECOGNIZED: + case GCR_ERROR_UNRECOGNIZED: message = _("Unrecognized or unsupported data."); break; - case GCR_PARSE_FAILURE: + case GCR_ERROR_FAILURE: message = _("Could not parse invalid or corrupted data."); break; - case GCR_PARSE_LOCKED: + case GCR_ERROR_LOCKED: message = _("The data is locked"); break; default: @@ -1595,37 +1586,11 @@ gcr_parser_parse_data (GcrParser *self, const guchar *data, break; }; - g_set_error_literal (err, GCR_PARSER_ERROR, args.result, message); + g_set_error_literal (err, GCR_DATA_ERROR, args.result, message); return FALSE; } gboolean -gcr_parser_parse_file (GcrParser *self, const gchar *filename, GError **err) -{ - GMappedFile *mapped; - gboolean ret; - const guchar *data; - gsize n_data; - - g_return_val_if_fail (GCR_IS_PARSER (self), FALSE); - g_return_val_if_fail (filename, FALSE); - g_return_val_if_fail (!err || !*err, FALSE); - - mapped = g_mapped_file_new (filename, FALSE, err); - if (mapped == NULL) - return FALSE; - - data = (const guchar*)g_mapped_file_get_contents (mapped); - n_data = g_mapped_file_get_length (mapped); - - ret = gcr_parser_parse_data (self, data, n_data, err); - - g_mapped_file_free (mapped); - - return ret; -} - -gboolean gcr_parser_format_enable (GcrParser *self, gint format_id) { ParserFormat *format; diff --git a/gcr/gcr-parser.h b/gcr/gcr-parser.h index 4038cf0..2f5be35 100644 --- a/gcr/gcr-parser.h +++ b/gcr/gcr-parser.h @@ -22,12 +22,12 @@ #ifndef __GCR_PARSER_H__ #define __GCR_PARSER_H__ +#include "gcr.h" + #include <glib-object.h> #include "gcr-types.h" -#define GCR_PARSER_ERROR (gcr_parser_get_error_domain ()) - #define GCR_TYPE_PARSER (gcr_parser_get_type ()) #define GCR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PARSER, GcrParser)) #define GCR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PARSER, GcrParserClass)) @@ -56,37 +56,31 @@ struct _GcrParserClass { void (*parsed) (GcrParser *self); }; -GType gcr_parser_get_type (void); - -GQuark gcr_parser_get_error_domain (void) G_GNUC_CONST; - -GcrParser* gcr_parser_new (void); +GType gcr_parser_get_type (void); -gboolean gcr_parser_format_enable (GcrParser *self, - gint format); +GcrParser* gcr_parser_new (void); -gboolean gcr_parser_format_disable (GcrParser *self, - gint format); +gboolean gcr_parser_format_enable (GcrParser *self, + gint format); -gboolean gcr_parser_format_supported (GcrParser *self, - gint format); +gboolean gcr_parser_format_disable (GcrParser *self, + gint format); -gboolean gcr_parser_parse_data (GcrParser *self, - const guchar *data, - gsize n_data, - GError **err); +gboolean gcr_parser_format_supported (GcrParser *self, + gint format); -gboolean gcr_parser_parse_file (GcrParser *self, - const gchar *filename, - GError **err); +gboolean gcr_parser_parse_data (GcrParser *self, + const guchar *data, + gsize n_data, + GError **err); -void gcr_parser_add_password (GcrParser *self, - const gchar *password); +void gcr_parser_add_password (GcrParser *self, + const gchar *password); -const gchar* gcr_parser_get_parsed_label (GcrParser *self); +const gchar* gcr_parser_get_parsed_label (GcrParser *self); -const gchar* gcr_parser_get_parsed_description (GcrParser *self); +const gchar* gcr_parser_get_parsed_description (GcrParser *self); -GP11Attributes* gcr_parser_get_parsed_attributes (GcrParser *self); +struct _GP11Attributes* gcr_parser_get_parsed_attributes (GcrParser *self); #endif /* __GCR_PARSER_H__ */ diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h index 5f12892..ca2ed91 100644 --- a/gcr/gcr-types.h +++ b/gcr/gcr-types.h @@ -1,11 +1,15 @@ #ifndef GCRTYPES_H_ #define GCRTYPES_H_ +#define GCR_DATA_ERROR (gcr_data_error_get_domain ()) + +GQuark gcr_data_error_get_domain (void) G_GNUC_CONST; + enum { - GCR_PARSE_FAILURE = -1, - GCR_PARSE_UNRECOGNIZED = 1, - GCR_PARSE_CANCELLED = 2, - GCR_PARSE_LOCKED = 3 + GCR_ERROR_FAILURE = -1, + GCR_ERROR_UNRECOGNIZED = 1, + GCR_ERROR_CANCELLED = 2, + GCR_ERROR_LOCKED = 3 }; enum { @@ -35,11 +39,8 @@ enum { GCR_FORMAT_PEM_PKCS12 }; -#ifndef GP11_H - /* Forward declare some of the GP11 objects */ -typedef struct _GP11Attributes GP11Attributes; - -#endif /* GP11_H */ +struct _GP11Attributes; +struct _GP11Slot; #endif /* GCRTYPES_H_ */ diff --git a/gcr/gcr.h b/gcr/gcr.h new file mode 100644 index 0000000..4cf2f23 --- /dev/null +++ b/gcr/gcr.h @@ -0,0 +1,46 @@ +/* + * gnome-keyring + * + * Copyright (C) 2008 Stefan Walter + * + * 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. + */ + +#ifndef __GCR_H__ +#define __GCR_H__ + +#include <glib.h> + +#ifndef GCR_API_SUBJECT_TO_CHANGE +#error "This API has not yet reached stability." +#endif + +struct _GP11Slot; + +#ifdef UNIMPLEMENTED +enum { + GCR_INIT_NO_MODULES = 0x01, +}; + +void gcr_initialize (guint flags); + +void gcr_modules_register_loaded (gpointer funcs); + +gboolean gcr_modules_register_file (const gchar *module_path, + GError *error); +#endif /* UNIMPLEMENTED */ + +#endif /* __GCR_H__ */ diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am index 63b89d8..4306474 100644 --- a/gcr/tests/Makefile.am +++ b/gcr/tests/Makefile.am @@ -8,6 +8,10 @@ UNIT_PROMPT = UNIT_LIBS = \ $(top_builddir)/gcr/libgcr.la \ $(top_builddir)/egg/libegg.la \ + $(top_builddir)/egg/libegg-secure-entry.la \ $(top_builddir)/gp11/libgp11.la +UNIT_FLAGS = \ + -DGCR_API_SUBJECT_TO_CHANGE + include $(top_srcdir)/tests/gtest.make diff --git a/gcr/tests/unit-test-parser.c b/gcr/tests/unit-test-parser.c index 70c11bc..3a60489 100644 --- a/gcr/tests/unit-test-parser.c +++ b/gcr/tests/unit-test-parser.c @@ -29,6 +29,8 @@ #include "gcr/gcr-parser.h" +#include "gp11/gp11.h" + #include <glib.h> #include <gcrypt.h> #include <libtasn1.h> |