diff options
Diffstat (limited to 'smime')
28 files changed, 8201 insertions, 0 deletions
diff --git a/smime/Makefile.am b/smime/Makefile.am new file mode 100644 index 0000000000..a358fdf7ea --- /dev/null +++ b/smime/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = lib gui + +-include $(top_srcdir)/git.mk diff --git a/smime/gui/Makefile.am b/smime/gui/Makefile.am new file mode 100644 index 0000000000..09c9fa8924 --- /dev/null +++ b/smime/gui/Makefile.am @@ -0,0 +1,54 @@ +privsolib_LTLIBRARIES = libevolution-smime.la + +libevolution_smime_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DG_LOG_DOMAIN=\"evolution-smime\" \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_srcdir)/smime/lib \ + -I$(top_builddir)/smime/lib \ + -I$(top_srcdir)/shell \ + -I$(top_builddir)/shell \ + -DEVOLUTION_DATADIR=\""$(datadir)"\" \ + -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_UIDIR=\""$(uidir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(CERT_UI_CFLAGS) + +libevolution_smime_la_SOURCES = \ + ca-trust-dialog.c \ + ca-trust-dialog.h \ + cert-trust-dialog.c \ + cert-trust-dialog.h \ + certificate-manager.c \ + certificate-manager.h \ + certificate-viewer.c \ + certificate-viewer.h \ + e-cert-selector.c \ + e-cert-selector.h \ + component.c \ + component.h + + +libevolution_smime_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(top_builddir)/shell/libevolution-shell.la \ + $(top_builddir)/smime/lib/libessmime.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(GTKHTML_LIBS) \ + $(CERT_UI_LIBS) + +libevolution_smime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) + +ui_DATA = smime-ui.ui + +EXTRA_DIST = \ + $(ui_DATA) + +-include $(top_srcdir)/git.mk diff --git a/smime/gui/ca-trust-dialog.c b/smime/gui/ca-trust-dialog.c new file mode 100644 index 0000000000..332b92bcd1 --- /dev/null +++ b/smime/gui/ca-trust-dialog.c @@ -0,0 +1,154 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ca-trust-dialog.h" +#include "certificate-manager.h" + +#include <glib/gi18n.h> + +#include "e-util/e-util.h" +#include "e-util/e-util-private.h" + +typedef struct { + GtkBuilder *builder; + GtkWidget *dialog; + GtkWidget *ssl_checkbutton; + GtkWidget *email_checkbutton; + GtkWidget *objsign_checkbutton; + + ECert *cert; +} CATrustDialogData; + +static void +free_data (gpointer data) +{ + CATrustDialogData *ctd = data; + + g_object_unref (ctd->cert); + g_object_unref (ctd->builder); + g_free (ctd); +} + +static void +catd_response (GtkWidget *w, + guint id, + CATrustDialogData *data) +{ + switch (id) { + case GTK_RESPONSE_ACCEPT: { + GtkWidget *dialog; + + dialog = e_cert_manager_new_certificate_viewer (GTK_WINDOW (data->dialog), data->cert); + + g_signal_stop_emission_by_name (w, "response"); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + break; } + } +} + +GtkWidget * +ca_trust_dialog_show (ECert *cert, + gboolean importing) +{ + CATrustDialogData *ctd_data; + GtkDialog *dialog; + GtkWidget *action_area; + GtkWidget *content_area; + GtkWidget *w; + gchar *txt; + + ctd_data = g_new0 (CATrustDialogData, 1); + + ctd_data->builder = gtk_builder_new (); + e_load_ui_builder_definition (ctd_data->builder, "smime-ui.ui"); + + ctd_data->dialog = e_builder_get_widget (ctd_data->builder, "ca-trust-dialog"); + + gtk_widget_ensure_style (ctd_data->dialog); + + dialog = GTK_DIALOG (ctd_data->dialog); + action_area = gtk_dialog_get_action_area (dialog); + content_area = gtk_dialog_get_content_area (dialog); + + gtk_container_set_border_width (GTK_CONTAINER (action_area), 12); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 0); + + ctd_data->cert = g_object_ref (cert); + + ctd_data->ssl_checkbutton = e_builder_get_widget (ctd_data->builder, "ssl_trust_checkbutton"); + ctd_data->email_checkbutton = e_builder_get_widget (ctd_data->builder, "email_trust_checkbutton"); + ctd_data->objsign_checkbutton = e_builder_get_widget (ctd_data->builder, "objsign_trust_checkbutton"); + + w = e_builder_get_widget (ctd_data->builder, "ca-trust-label"); + txt = g_strdup_printf (_("Certificate '%s' is a CA certificate.\n\nEdit trust settings:"), e_cert_get_cn (cert)); + gtk_label_set_text ((GtkLabel *) w, txt); + g_free (txt); + + g_signal_connect ( + ctd_data->dialog, "response", + G_CALLBACK (catd_response), ctd_data); + + g_object_set_data_full (G_OBJECT (ctd_data->dialog), "CATrustDialogData", ctd_data, free_data); + + return ctd_data->dialog; +} + +void +ca_trust_dialog_set_trust (GtkWidget *widget, + gboolean ssl, + gboolean email, + gboolean objsign) +{ + CATrustDialogData *ctd_data; + + ctd_data = g_object_get_data (G_OBJECT (widget), "CATrustDialogData"); + if (!ctd_data) + return; + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ctd_data->ssl_checkbutton), ssl); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ctd_data->email_checkbutton), email); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ctd_data->objsign_checkbutton), objsign); +} + +void +ca_trust_dialog_get_trust (GtkWidget *widget, + gboolean *ssl, + gboolean *email, + gboolean *objsign) +{ + CATrustDialogData *ctd_data; + + ctd_data = g_object_get_data (G_OBJECT (widget), "CATrustDialogData"); + if (!ctd_data) + return; + + *ssl = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ctd_data->ssl_checkbutton)); + *email = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ctd_data->email_checkbutton)); + *objsign = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ctd_data->objsign_checkbutton)); +} diff --git a/smime/gui/ca-trust-dialog.h b/smime/gui/ca-trust-dialog.h new file mode 100644 index 0000000000..62b842d945 --- /dev/null +++ b/smime/gui/ca-trust-dialog.h @@ -0,0 +1,35 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef CA_TRUST_DIALOG_H +#define CA_TRUST_DIALOG_H + +#include <gtk/gtk.h> +#include "e-cert.h" + +GtkWidget * ca_trust_dialog_show (ECert *cert, gboolean importing); + +void ca_trust_dialog_set_trust (GtkWidget *widget, gboolean ssl, gboolean email, gboolean objsign); +void ca_trust_dialog_get_trust (GtkWidget *widget, gboolean *ssl, gboolean *email, gboolean *objsign); + +#endif /* CA_TRUST_DIALOG_H */ diff --git a/smime/gui/cert-trust-dialog.c b/smime/gui/cert-trust-dialog.c new file mode 100644 index 0000000000..366a79748e --- /dev/null +++ b/smime/gui/cert-trust-dialog.c @@ -0,0 +1,162 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-cert.h" +#include "e-cert-trust.h" +#include "e-cert-db.h" +#include "cert-trust-dialog.h" +#include "ca-trust-dialog.h" + +#include <gtk/gtk.h> + +#include <glib/gi18n.h> + +#include "e-util/e-util.h" +#include "e-util/e-util-private.h" + +typedef struct { + GtkBuilder *builder; + GtkWidget *dialog; + GtkWidget *trust_button; + GtkWidget *notrust_button; + GtkWidget *label; + + ECert *cert, *cacert; +} CertTrustDialogData; + +static void +free_data (gpointer data) +{ + CertTrustDialogData *ctd = data; + + g_object_unref (ctd->cert); + g_object_unref (ctd->cacert); + g_object_unref (ctd->builder); + g_free (ctd); +} + +static void +ctd_response (GtkWidget *w, + guint id, + CertTrustDialogData *data) +{ + CERTCertTrust trust; + CERTCertificate *icert; + + switch (id) { + case GTK_RESPONSE_OK: + icert = e_cert_get_internal_cert (data->cert); + e_cert_trust_init (&trust); + e_cert_trust_set_valid_peer (&trust); + e_cert_trust_add_peer_trust ( + &trust, FALSE, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->trust_button)), + FALSE); + e_cert_db_change_cert_trust (icert, &trust); + break; + case GTK_RESPONSE_ACCEPT: { + /* just *what on earth* was chris thinking here!?!?! copied from certificate-manager.c */ + GtkWidget *dialog = ca_trust_dialog_show (data->cacert, FALSE); + /* *icert is already declared earlier in this function? */ + CERTCertificate *icert = e_cert_get_internal_cert (data->cacert); + + g_signal_stop_emission_by_name (w, "response"); + + ca_trust_dialog_set_trust ( + dialog, + e_cert_trust_has_trusted_ca (icert->trust, TRUE, FALSE, FALSE), + e_cert_trust_has_trusted_ca (icert->trust, FALSE, TRUE, FALSE), + e_cert_trust_has_trusted_ca (icert->trust, FALSE, FALSE, TRUE)); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + gboolean trust_ssl, trust_email, trust_objsign; + + ca_trust_dialog_get_trust ( + dialog, + &trust_ssl, &trust_email, &trust_objsign); + + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trust_add_ca_trust ( + &trust, + trust_ssl, + trust_email, + trust_objsign); + + e_cert_db_change_cert_trust (icert, &trust); + } + + gtk_widget_destroy (dialog); + break; } + } +} + +GtkWidget * +cert_trust_dialog_show (ECert *cert) +{ + CertTrustDialogData *ctd_data; + CERTCertificate *icert; + + ctd_data = g_new0 (CertTrustDialogData, 1); + + ctd_data->builder = gtk_builder_new (); + e_load_ui_builder_definition (ctd_data->builder, "smime-ui.ui"); + + ctd_data->dialog = e_builder_get_widget (ctd_data->builder, "cert-trust-dialog"); + ctd_data->cert = g_object_ref (cert); + ctd_data->cacert = e_cert_get_ca_cert (cert); + ctd_data->trust_button = e_builder_get_widget (ctd_data->builder, "cert-trust"); + ctd_data->notrust_button = e_builder_get_widget (ctd_data->builder, "cert-notrust"); + + ctd_data->label = e_builder_get_widget (ctd_data->builder, "trust-label"); + + g_signal_connect ( + ctd_data->dialog, "response", + G_CALLBACK (ctd_response), ctd_data); + + g_object_set_data_full (G_OBJECT (ctd_data->dialog), "CertTrustDialogData", ctd_data, free_data); + + icert = e_cert_get_internal_cert (cert); + if (e_cert_trust_has_trusted_peer (icert->trust, FALSE, TRUE, FALSE)) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ctd_data->trust_button), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ctd_data->notrust_button), TRUE); + + icert = e_cert_get_internal_cert (ctd_data->cacert); + if (e_cert_trust_has_trusted_ca (icert->trust, FALSE, TRUE, FALSE)) + gtk_label_set_text ( + (GtkLabel *) ctd_data->label, + _("Because you trust the certificate authority that issued this certificate, " + "then you trust the authenticity of this certificate unless otherwise indicated here")); + else + gtk_label_set_text ( + (GtkLabel *) ctd_data->label, + _("Because you do not trust the certificate authority that issued this certificate, " + "then you do not trust the authenticity of this certificate unless otherwise indicated here")); + + return ctd_data->dialog; +} diff --git a/smime/gui/cert-trust-dialog.h b/smime/gui/cert-trust-dialog.h new file mode 100644 index 0000000000..20a076b7e9 --- /dev/null +++ b/smime/gui/cert-trust-dialog.h @@ -0,0 +1,35 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _CERT_TRUST_DIALOG_H_ +#define _CERT_TRUST_DIALOG_H + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +GtkWidget * cert_trust_dialog_show (ECert *cert); + +G_END_DECLS + +#endif /* _CERT_TRUST_DIALOG_H_ */ diff --git a/smime/gui/certificate-manager.c b/smime/gui/certificate-manager.c new file mode 100644 index 0000000000..38645d2cdf --- /dev/null +++ b/smime/gui/certificate-manager.c @@ -0,0 +1,1139 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> + +#include <glib/gi18n.h> + +#include "ca-trust-dialog.h" +#include "cert-trust-dialog.h" +#include "certificate-manager.h" +#include "certificate-viewer.h" + +#include "e-cert.h" +#include "e-cert-trust.h" +#include "e-cert-db.h" + +#include "nss.h" +#include <cms.h> +#include <cert.h> +#include <certdb.h> +#include <pkcs11.h> +#include <pk11func.h> + +#include "shell/e-shell.h" + +#define E_CERT_MANAGER_CONFIG_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CERT_MANAGER_CONFIG, ECertManagerConfigPrivate)) + +G_DEFINE_TYPE (ECertManagerConfig, e_cert_manager_config, GTK_TYPE_BOX); + +enum { + PROP_0, + PROP_PREFERENCES_WINDOW +}; + +#define ECMC_TREE_VIEW(o) ecmc->priv->o->treeview +#define PAGE_TREE_VIEW(o) o->treeview + +typedef struct { + GType type; + const gchar *column_title; + const gchar * (*get_cert_data_func) (ECert *cert); /* Prototype to e_cert_get_ * functions */ + gboolean visible; /* Default visibility of column */ +} CertTreeColumn; + +static CertTreeColumn yourcerts_columns[] = { + + { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE }, + { G_TYPE_STRING, N_("Issued To Organization"), e_cert_get_org, FALSE }, + { G_TYPE_STRING, N_("Issued To Organizational Unit"), e_cert_get_org_unit, FALSE }, + { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE }, + { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE }, + { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, TRUE }, + { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE }, + { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE }, + { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE }, + { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE }, + { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE }, + { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE }, + { G_TYPE_OBJECT, NULL, NULL, FALSE } /* Hidden column for ECert * object */ + +}; +static const gchar * yourcerts_mime_types[] = { "application/x-x509-user-cert", "application/x-pkcs12", NULL }; + +static CertTreeColumn contactcerts_columns[] = { + + { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE }, + { G_TYPE_STRING, N_("Email Address"), e_cert_get_email, TRUE }, + { G_TYPE_STRING, N_("Issued To Organization"), e_cert_get_org, FALSE }, + { G_TYPE_STRING, N_("Issued To Organizational Unit"), e_cert_get_org_unit, FALSE }, + { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE }, + { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE }, + { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, TRUE }, + { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE }, + { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE }, + { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE }, + { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE }, + { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE }, + { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE }, + { G_TYPE_OBJECT, NULL, NULL, FALSE } + +}; +static const gchar * contactcerts_mime_types[] = { "application/x-x509-email-cert", "application/x-x509-ca-cert", NULL }; + +static CertTreeColumn authoritycerts_columns[] = { + + { G_TYPE_STRING, N_("Certificate Name"), e_cert_get_cn, TRUE }, + { G_TYPE_STRING, N_("Email Address"), e_cert_get_email, TRUE }, + { G_TYPE_STRING, N_("Serial Number"), e_cert_get_serial_number, TRUE }, + { G_TYPE_STRING, N_("Purposes"), e_cert_get_usage, TRUE }, + { G_TYPE_STRING, N_("Issued By"), e_cert_get_issuer_cn, FALSE }, + { G_TYPE_STRING, N_("Issued By Organization"), e_cert_get_issuer_org, FALSE }, + { G_TYPE_STRING, N_("Issued By Organizational Unit"), e_cert_get_issuer_org_unit, FALSE }, + { G_TYPE_STRING, N_("Issued"), e_cert_get_issued_on, FALSE }, + { G_TYPE_STRING, N_("Expires"), e_cert_get_expires_on, TRUE }, + { G_TYPE_STRING, N_("SHA1 Fingerprint"), e_cert_get_sha1_fingerprint, FALSE }, + { G_TYPE_STRING, N_("MD5 Fingerprint"), e_cert_get_md5_fingerprint, FALSE }, + { G_TYPE_OBJECT, NULL, NULL, FALSE } + +}; +static const gchar * authoritycerts_mime_types[] = { "application/x-x509-ca-cert", NULL }; + +typedef struct { + GtkTreeView *treeview; + GtkTreeModel *streemodel; + GHashTable *root_hash; + GtkMenu *popup_menu; + GtkWidget *view_button; + GtkWidget *edit_button; + GtkWidget *backup_button; + GtkWidget *backup_all_button; + GtkWidget *import_button; + GtkWidget *delete_button; + + CertTreeColumn *columns; + gint columns_count; + + ECertType cert_type; + const gchar *cert_filter_name; + const gchar **cert_mime_types; +} CertPage; + +struct _ECertManagerConfigPrivate { + GtkBuilder *builder; + + EPreferencesWindow *pref_window; + + CertPage *yourcerts_page; + CertPage *contactcerts_page; + CertPage *authoritycerts_page; +}; + +static void view_cert (GtkWidget *button, CertPage *cp); +static void edit_cert (GtkWidget *button, CertPage *cp); +static void delete_cert (GtkWidget *button, CertPage *cp); +static void import_cert (GtkWidget *button, CertPage *cp); + +static void load_certs (CertPage *cp); +static void unload_certs (CertPage *cp); + +static void +save_treeview_state (GtkTreeView *treeview) +{ + GKeyFile *keyfile; + GtkTreeModel *model; + GtkTreeSortable *sortable; + GtkSortType sort_type; + gint columns_count; + gint i = 0; + gint *list; + gchar *cfg_file, *data; + const gchar *tree_name; + + g_return_if_fail (treeview && GTK_IS_TREE_VIEW (treeview)); + + model = gtk_tree_view_get_model (treeview); + g_return_if_fail (model && GTK_IS_TREE_MODEL_SORT (model)); + + keyfile = g_key_file_new (); + cfg_file = g_build_filename (e_get_user_config_dir (), "cert_trees.ini", NULL); + g_key_file_load_from_file (keyfile, cfg_file, 0, NULL); + + tree_name = gtk_widget_get_name (GTK_WIDGET (treeview)); + sortable = GTK_TREE_SORTABLE (model); + + columns_count = gtk_tree_model_get_n_columns (model) - 1; /* Ignore the last column - the ECert * holder */ + list = g_new0 (gint, columns_count); + + for (i = 0; i < columns_count; i++) { + GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i); + if (gtk_tree_view_column_get_visible (column)) { + list[gtk_tree_view_column_get_sort_column_id (column)] = gtk_tree_view_column_get_width (column); + } else { + list[gtk_tree_view_column_get_sort_column_id (column)] = 0; + } + } + g_key_file_set_integer_list (keyfile, tree_name, "columns", list, columns_count); + g_free (list); + + list = g_new0 (gint, columns_count); + for (i = 0; i < columns_count; i++) { + GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i); + list[i] = gtk_tree_view_column_get_sort_column_id (column); + } + g_key_file_set_integer_list (keyfile, tree_name, "columns-order", list, columns_count); + g_free (list); + + gtk_tree_sortable_get_sort_column_id (sortable, &i, &sort_type); + g_key_file_set_integer (keyfile, tree_name, "sort-column", i); + + g_key_file_set_integer (keyfile, tree_name, "sort-order", sort_type); + + data = g_key_file_to_data (keyfile, NULL, NULL); + g_file_set_contents (cfg_file, data, -1, NULL); + + g_free (data); + g_free (cfg_file); + g_key_file_free (keyfile); +} + +static void +load_treeview_state (GtkTreeView *treeview) +{ + GKeyFile *keyfile; + gint i, *list; + gsize length; + GtkTreeSortable *sortable; + GtkTreeModel *model; + gchar *cfg_file; + const gchar *tree_name; + + g_return_if_fail (treeview && GTK_IS_TREE_VIEW (treeview)); + + keyfile = g_key_file_new (); + cfg_file = g_build_filename (e_get_user_config_dir (), "cert_trees.ini", NULL); + + if (!g_key_file_load_from_file (keyfile, cfg_file, 0, NULL)) { + g_key_file_free (keyfile); + g_free (cfg_file); + return; + } + + model = GTK_TREE_MODEL (gtk_tree_view_get_model (treeview)); + tree_name = gtk_widget_get_name (GTK_WIDGET (treeview)); + list = g_key_file_get_integer_list (keyfile, tree_name, "columns", &length, NULL); + + if (list) { + gboolean all_hidden = TRUE; + + if (length != (gtk_tree_model_get_n_columns (model) - 1)) { + g_debug ("%s: Unexpected number of columns in config file", G_STRFUNC); + g_free (list); + goto exit; + } + + for (i = 0; all_hidden && i < length; i++) { + all_hidden = list[i] == 0; + } + + for (i = 0; !all_hidden && i < length; i++) { + GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview, i); + if (list[i]) { + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, list[i]); + gtk_tree_view_column_set_visible (column, TRUE); + } else { + gtk_tree_view_column_set_visible (column, FALSE); + } + } + g_free (list); + } + + list = g_key_file_get_integer_list (keyfile, tree_name, "columns-order", &length, NULL); + + if (list) { + GList *columns = gtk_tree_view_get_columns (treeview); + + if (length != g_list_length (columns)) { + g_debug ("%s: Unexpected number of columns in config file", G_STRFUNC); + g_free (list); + goto exit; + } + + for (i = (length - 1); i >= 0; i--) { + if ((list[i] >= 0) && (list[i] < length)) { + GtkTreeViewColumn *column = g_list_nth (columns, list[i])->data; + gtk_tree_view_move_column_after (treeview, column, NULL); + } else { + g_warning ("%s: Invalid column number", G_STRFUNC); + } + } + g_free (list); + g_list_free (columns); + } + + sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (treeview)); + gtk_tree_sortable_set_sort_column_id ( + sortable, + g_key_file_get_integer (keyfile, tree_name, "sort-column", 0), + g_key_file_get_integer (keyfile, tree_name, "sort-order", GTK_SORT_ASCENDING)); + + exit: + g_free (cfg_file); + g_key_file_free (keyfile); +} + +static void +report_and_free_error (CertPage *cp, + const gchar *where, + GError *error) +{ + g_return_if_fail (cp != NULL); + + e_notice ( + gtk_widget_get_toplevel (GTK_WIDGET (cp->treeview)), + GTK_MESSAGE_ERROR, "%s: %s", where, + error ? error->message : _("Unknown error")); + + if (error) + g_error_free (error); +} + +static gboolean +treeview_header_clicked (GtkWidget *widget, + GdkEvent *button_event, + gpointer user_data) +{ + GtkMenu *menu = user_data; + guint event_button = 0; + guint32 event_time; + + gdk_event_get_button (button_event, &event_button); + event_time = gdk_event_get_time (button_event); + + if (event_button != 3) + return FALSE; + + gtk_widget_show_all (GTK_WIDGET (menu)); + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button, event_time); + + return TRUE; +} + +static void +header_popup_item_toggled (GtkCheckMenuItem *item, + gpointer user_data) +{ + GtkTreeViewColumn *column = user_data; + + gtk_tree_view_column_set_visible ( + column, + gtk_check_menu_item_get_active (item)); +} + +static void +treeview_column_visibility_changed (GtkTreeViewColumn *column, + GParamSpec *pspec, + gpointer user_data) +{ + GtkCheckMenuItem *menu_item = user_data; + + gtk_check_menu_item_set_active ( + menu_item, + gtk_tree_view_column_get_visible (column)); + +} + +static void +treeview_selection_changed (GtkTreeSelection *selection, + CertPage *cp) +{ + GtkTreeIter iter; + gboolean cert_selected = FALSE; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + ECert *cert; + + gtk_tree_model_get ( + model, &iter, + cp->columns_count - 1, &cert, + -1); + + if (cert) { + cert_selected = TRUE; + g_object_unref (cert); + } + } + + if (cp->delete_button) + gtk_widget_set_sensitive (cp->delete_button, cert_selected); + if (cp->edit_button) + gtk_widget_set_sensitive (cp->edit_button, cert_selected); + if (cp->view_button) + gtk_widget_set_sensitive (cp->view_button, cert_selected); +} + +static void +treeview_add_column (CertPage *cp, + gint column_index) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + GtkWidget *header, *item; + + if (cp->columns[column_index].type != G_TYPE_STRING) + return; + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + column = gtk_tree_view_column_new_with_attributes ( + gettext (cp->columns[column_index].column_title), + cell, "text", column_index, NULL); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_reorderable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, column_index); + gtk_tree_view_column_set_visible (column, cp->columns[column_index].visible); + gtk_tree_view_append_column (cp->treeview, column); + + header = gtk_tree_view_column_get_button (column); + g_signal_connect ( + header, "button-release-event", + G_CALLBACK (treeview_header_clicked), cp->popup_menu); + + /* The first column should not be concealable so there's no point in displaying + * it in the popup menu */ + if (column_index == 0) + return; + + /* Add item to header popup */ + item = gtk_check_menu_item_new_with_label ( + gettext (cp->columns[column_index].column_title)); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), cp->columns[column_index].visible); + gtk_menu_attach (cp->popup_menu, item, 0, 1, column_index - 1, column_index); + g_signal_connect ( + item, "toggled", + G_CALLBACK (header_popup_item_toggled), column); + g_signal_connect ( + column, "notify::visible", + G_CALLBACK (treeview_column_visibility_changed), item); +} + +struct find_cert_data { + ECert *cert; + GtkTreePath *path; + CertPage *cp; +}; + +static gboolean +find_cert_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + struct find_cert_data *fcd = data; + ECert *cert = NULL; + + g_return_val_if_fail (model != NULL, TRUE); + g_return_val_if_fail (iter != NULL, TRUE); + g_return_val_if_fail (data != NULL, TRUE); + + /* Get the certificate object from model */ + gtk_tree_model_get (model, iter, (fcd->cp->columns_count - 1), &cert, -1); + + if (cert && g_strcmp0 (e_cert_get_serial_number (cert), e_cert_get_serial_number (fcd->cert)) == 0 + && g_strcmp0 (e_cert_get_subject_name (cert), e_cert_get_subject_name (fcd->cert)) == 0 + && g_strcmp0 (e_cert_get_sha1_fingerprint (cert), e_cert_get_sha1_fingerprint (fcd->cert)) == 0 + && g_strcmp0 (e_cert_get_md5_fingerprint (cert), e_cert_get_md5_fingerprint (fcd->cert)) == 0) { + fcd->path = gtk_tree_path_copy (path); + } + + if (cert) + g_object_unref (cert); + + return fcd->path != NULL; +} + +static void +select_certificate (CertPage *cp, + ECert *cert) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + struct find_cert_data fcd; + + g_return_if_fail (cp != NULL); + g_return_if_fail (cert != NULL); + g_return_if_fail (E_IS_CERT (cert)); + + model = gtk_tree_view_get_model (cp->treeview); + g_return_if_fail (model != NULL); + + fcd.cp = cp; + fcd.cert = cert; + fcd.path = NULL; + + gtk_tree_model_foreach (model, find_cert_cb, &fcd); + + if (fcd.path) { + gtk_tree_view_expand_to_path (cp->treeview, fcd.path); + + selection = gtk_tree_view_get_selection (cp->treeview); + gtk_tree_selection_select_path (selection, fcd.path); + + gtk_tree_view_scroll_to_cell (cp->treeview, fcd.path, NULL, TRUE, 0.5, 0.5); + gtk_tree_path_free (fcd.path); + } +} + +static void +view_cert (GtkWidget *button, + CertPage *cp) +{ + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) { + ECert *cert; + + gtk_tree_model_get ( + GTK_TREE_MODEL (cp->streemodel), &iter, + cp->columns_count - 1, &cert, + -1); + + if (cert) { + GtkWidget *dialog, *parent; + + parent = gtk_widget_get_toplevel (button); + if (!parent || !GTK_IS_WINDOW (parent)) + parent = NULL; + + dialog = e_cert_manager_new_certificate_viewer ((GtkWindow *) parent, cert); + g_signal_connect ( + dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show (dialog); + g_object_unref (cert); + } + } +} + +static void +edit_cert (GtkWidget *button, + CertPage *cp) +{ + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) { + ECert *cert; + + gtk_tree_model_get ( + GTK_TREE_MODEL (cp->streemodel), &iter, + cp->columns_count - 1, &cert, + -1); + + if (cert) { + GtkWidget *dialog; + CERTCertificate *icert = e_cert_get_internal_cert (cert); + + switch (cp->cert_type) { + case E_CERT_CA: + dialog = ca_trust_dialog_show (cert, FALSE); + ca_trust_dialog_set_trust ( + dialog, + e_cert_trust_has_trusted_ca (icert->trust, TRUE, FALSE, FALSE), + e_cert_trust_has_trusted_ca (icert->trust, FALSE, TRUE, FALSE), + e_cert_trust_has_trusted_ca (icert->trust, FALSE, FALSE, TRUE)); + break; + case E_CERT_CONTACT: + dialog = cert_trust_dialog_show (cert); + break; + default: + /* Other cert types cannot be edited */ + return; + } + + if ((gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) && + (cp->cert_type == E_CERT_CA)) { + gboolean trust_ssl, trust_email, trust_objsign; + CERTCertTrust trust; + + ca_trust_dialog_get_trust ( + dialog, + &trust_ssl, &trust_email, &trust_objsign); + + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trust_add_ca_trust ( + &trust, + trust_ssl, trust_email, trust_objsign); + + e_cert_db_change_cert_trust (icert, &trust); + } + + gtk_widget_destroy (dialog); + g_object_unref (cert); + } + } +} + +static void +import_cert (GtkWidget *button, + CertPage *cp) +{ + GtkWidget *filesel; + GtkFileFilter *filter; + gint i; + + filesel = gtk_file_chooser_dialog_new ( + _("Select a certificate to import..."), NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + gtk_dialog_set_default_response (GTK_DIALOG (filesel), GTK_RESPONSE_OK); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, cp->cert_filter_name); + for (i = 0; cp->cert_mime_types[i] != NULL; i++) { + gtk_file_filter_add_mime_type (filter, cp->cert_mime_types[i]); + } + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (filesel), filter); + + if (gtk_dialog_run (GTK_DIALOG (filesel)) == GTK_RESPONSE_OK) { + gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filesel)); + GSList *imported_certs = NULL; + GError *error = NULL; + gboolean import; + + /* destroy dialog to get rid of it in the GUI */ + gtk_widget_destroy (filesel); + + switch (cp->cert_type) { + case E_CERT_USER: + import = e_cert_db_import_pkcs12_file (e_cert_db_peek (), filename, &error); + break; + case E_CERT_CONTACT: + case E_CERT_CA: + import = e_cert_db_import_certs_from_file ( + e_cert_db_peek (), filename, + cp->cert_type, &imported_certs, &error); + break; + default: + g_free (filename); + return; + } + + if (import) { + unload_certs (cp); + load_certs (cp); + + if (imported_certs) + select_certificate (cp, imported_certs->data); + + } else { + report_and_free_error (cp, _("Failed to import certificate"), error); + } + + g_slist_foreach (imported_certs, (GFunc) g_object_unref, NULL); + g_slist_free (imported_certs); + g_free (filename); + } else + gtk_widget_destroy (filesel); +} + +static void +delete_cert (GtkWidget *button, + CertPage *cp) +{ + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (cp->treeview), NULL, &iter)) { + ECert *cert; + + gtk_tree_model_get ( + GTK_TREE_MODEL (cp->streemodel), &iter, + cp->columns_count - 1, &cert, + -1); + + if (cert && e_cert_db_delete_cert (e_cert_db_peek (), cert)) { + GtkTreeIter child_iter, parent_iter; + gboolean has_parent; + GtkTreeStore *store = GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cp->streemodel))); + + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (cp->streemodel), &child_iter, &iter); + has_parent = gtk_tree_model_iter_parent (GTK_TREE_MODEL (store), &parent_iter, &child_iter); + gtk_tree_store_remove (store, &child_iter); + + /* Remove parent if it became empty */ + if (has_parent && gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &parent_iter) == 0) + gtk_tree_store_remove (store, &parent_iter); + + /* we need two unrefs here, one to unref the + * gtk_tree_model_get above, and one to unref + * the initial ref when we created the cert + * and added it to the tree */ + g_object_unref (cert); + g_object_unref (cert); + } else if (cert) { + g_object_unref (cert); + } + } + +} + +static void +add_cert (CertPage *cp, + ECert *cert) +{ + GtkTreeIter iter; + GtkTreeIter *parent_iter = NULL; + const gchar *organization = e_cert_get_org (cert); + GtkTreeModel *model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (cp->streemodel)); + gint i; + + if (organization) { + parent_iter = g_hash_table_lookup (cp->root_hash, organization); + if (!parent_iter) { + /* create a new toplevel node */ + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); + + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + 0, organization, -1); + + /* now copy it off into parent_iter and insert it into + * the hashtable */ + parent_iter = gtk_tree_iter_copy (&iter); + g_hash_table_insert (cp->root_hash, g_strdup (organization), parent_iter); + } + } + + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent_iter); + + for (i = 0; i < cp->columns_count; i++) { + const gchar * (*get_cert_data_func) (ECert *cert); + + /* When e_cert_get_cn() is empty, use _get_nickname() */ + if ((cp->columns[i].get_cert_data_func == e_cert_get_cn) && (!e_cert_get_cn (cert))) { + get_cert_data_func = e_cert_get_nickname; + } else { + get_cert_data_func = cp->columns[i].get_cert_data_func; + } + + if (cp->columns[i].type == G_TYPE_STRING) { + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + i, get_cert_data_func (cert), -1); + } else if (cp->columns[i].type == G_TYPE_OBJECT) { + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + i, cert, -1); + } + } +} + +static void +unload_certs (CertPage *cp) +{ + GtkTreeStore *treemodel; + GType types[cp->columns_count]; + gint i; + + g_return_if_fail (cp != NULL); + + for (i = 0; i < cp->columns_count; i++) + types[i] = cp->columns[i].type; + treemodel = gtk_tree_store_newv (cp->columns_count, types); + + if (cp->streemodel) + g_object_unref (cp->streemodel); + + cp->streemodel = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (treemodel)); + + g_object_unref (treemodel); + gtk_tree_view_set_model (cp->treeview, cp->streemodel); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (cp->streemodel), 0, GTK_SORT_ASCENDING); + + if (cp->root_hash) + g_hash_table_destroy (cp->root_hash); + + cp->root_hash = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gtk_tree_iter_free); +} + +static void +load_certs (CertPage *cp) +{ + CERTCertList *certList; + CERTCertListNode *node; + + g_return_if_fail (cp != NULL); + + certList = PK11_ListCerts (PK11CertListUnique, NULL); + + for (node = CERT_LIST_HEAD (certList); + !CERT_LIST_END (node, certList); + node = CERT_LIST_NEXT (node)) { + ECert *cert = e_cert_new (CERT_DupCertificate ((CERTCertificate *) node->cert)); + ECertType ct = e_cert_get_cert_type (cert); + + /* show everything else in a contact tab */ + if (ct == cp->cert_type || (cp->cert_type == E_CERT_CONTACT && ct != E_CERT_CA && ct != E_CERT_USER)) { + add_cert (cp, cert); + } else { + g_object_unref (cert); + } + } + + CERT_DestroyCertList (certList); +} + +static gboolean +populate_ui (ECertManagerConfig *ecmc) +{ + /* This is an idle callback. */ + + ECertManagerConfigPrivate *priv = ecmc->priv; + + unload_certs (priv->yourcerts_page); + load_certs (priv->yourcerts_page); + + unload_certs (priv->contactcerts_page); + load_certs (priv->contactcerts_page); + + unload_certs (priv->authoritycerts_page); + load_certs (priv->authoritycerts_page); + + /* expand all three trees */ + gtk_tree_view_expand_all (ECMC_TREE_VIEW (yourcerts_page)); + gtk_tree_view_expand_all (ECMC_TREE_VIEW (contactcerts_page)); + gtk_tree_view_expand_all (ECMC_TREE_VIEW (authoritycerts_page)); + + /* Now load settings of each treeview */ + load_treeview_state (ECMC_TREE_VIEW (yourcerts_page)); + load_treeview_state (ECMC_TREE_VIEW (contactcerts_page)); + load_treeview_state (ECMC_TREE_VIEW (authoritycerts_page)); + + return FALSE; +} + +static void +initialize_ui (CertPage *cp) +{ + GtkTreeSelection *selection; + gint i; + + cp->popup_menu = GTK_MENU (gtk_menu_new ()); + + /* Add columns to treeview */ + for (i = 0; i < cp->columns_count; i++) + treeview_add_column (cp, i); + + selection = gtk_tree_view_get_selection (cp->treeview); + g_signal_connect ( + selection, "changed", + G_CALLBACK (treeview_selection_changed), cp); + + if (cp->import_button) + g_signal_connect ( + cp->import_button, "clicked", + G_CALLBACK (import_cert), cp); + + if (cp->edit_button) + g_signal_connect ( + cp->edit_button, "clicked", + G_CALLBACK (edit_cert), cp); + + if (cp->delete_button) + g_signal_connect ( + cp->delete_button, "clicked", + G_CALLBACK (delete_cert), cp); + + if (cp->view_button) + g_signal_connect ( + cp->view_button, "clicked", + G_CALLBACK (view_cert), cp); +} + +static void +cert_manager_config_window_hide (ECertManagerConfig *ecmc, + EPreferencesWindow *epw) +{ + g_return_if_fail (ecmc); + + save_treeview_state (ECMC_TREE_VIEW (yourcerts_page)); + save_treeview_state (ECMC_TREE_VIEW (contactcerts_page)); + save_treeview_state (ECMC_TREE_VIEW (authoritycerts_page)); +} + +static void +free_cert (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + CertPage *cp = user_data; + ECert *cert; + + gtk_tree_model_get (model, iter, cp->columns_count - 1, &cert, -1); + + /* Double unref: one for gtk_tree_model_get() and one for e_cert_new() */ + g_object_unref (cert); + g_object_unref (cert); +} + +static void +cert_page_free (CertPage *cp) +{ + if (!cp) + return; + + if (cp->streemodel) { + gtk_tree_model_foreach (GTK_TREE_MODEL (cp->streemodel), + (GtkTreeModelForeachFunc) free_cert, cp); + g_object_unref (cp->streemodel); + cp->streemodel = NULL; + } + + if (cp->root_hash) { + g_hash_table_unref (cp->root_hash); + cp->root_hash = NULL; + } + + g_free (cp); +} + +static void +cert_manager_config_dispose (GObject *object) +{ + ECertManagerConfig *ecmc = E_CERT_MANAGER_CONFIG (object); + + if (ecmc->priv->yourcerts_page) { + cert_page_free (ecmc->priv->yourcerts_page); + ecmc->priv->yourcerts_page = NULL; + } + + if (ecmc->priv->contactcerts_page) { + cert_page_free (ecmc->priv->contactcerts_page); + ecmc->priv->contactcerts_page = NULL; + } + + if (ecmc->priv->authoritycerts_page) { + cert_page_free (ecmc->priv->authoritycerts_page); + ecmc->priv->authoritycerts_page = NULL; + } + + if (ecmc->priv->builder) { + g_object_unref (ecmc->priv->builder); + ecmc->priv->builder = NULL; + } + + if (ecmc->priv->pref_window) { + g_signal_handlers_disconnect_matched (ecmc->priv->pref_window, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, ecmc); + ecmc->priv->pref_window = NULL; + } + + G_OBJECT_CLASS (e_cert_manager_config_parent_class)->dispose (object); +} + +static void +cert_manager_config_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ECertManagerConfig *ecmc = E_CERT_MANAGER_CONFIG (object); + + switch (property_id) { + case PROP_PREFERENCES_WINDOW: + ecmc->priv->pref_window = g_value_get_object (value); + /* When the preferences window is "closed" (= hidden), save + * state of all treeviews. */ + g_signal_connect_swapped ( + ecmc->priv->pref_window, "hide", + G_CALLBACK (cert_manager_config_window_hide), ecmc); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_cert_manager_config_class_init (ECertManagerConfigClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ECertManagerConfigPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = cert_manager_config_set_property; + object_class->dispose = cert_manager_config_dispose; + + g_object_class_install_property ( + object_class, + PROP_PREFERENCES_WINDOW, + g_param_spec_object ( + "preferences-window", + NULL, + NULL, + E_TYPE_PREFERENCES_WINDOW, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); +} + +static void +e_cert_manager_config_init (ECertManagerConfig *ecmc) +{ + ECertManagerConfigPrivate *priv; + GtkWidget *parent, *widget; + CertPage *cp; + + priv = E_CERT_MANAGER_CONFIG_GET_PRIVATE (ecmc); + ecmc->priv = priv; + + /* We need to peek the db here to make sure it (and NSS) are fully initialized. */ + e_cert_db_peek (); + + priv->builder = gtk_builder_new (); + e_load_ui_builder_definition (priv->builder, "smime-ui.ui"); + + cp = g_new0 (CertPage, 1); + priv->yourcerts_page = cp; + cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "yourcerts-treeview")); + cp->streemodel = NULL; + cp->view_button = e_builder_get_widget (priv->builder, "your-view-button"); + cp->backup_button = e_builder_get_widget (priv->builder, "your-backup-button"); + cp->backup_all_button = e_builder_get_widget (priv->builder, "your-backup-all-button"); + cp->edit_button = NULL; + cp->import_button = e_builder_get_widget (priv->builder, "your-import-button"); + cp->delete_button = e_builder_get_widget (priv->builder, "your-delete-button"); + cp->columns = yourcerts_columns; + cp->columns_count = G_N_ELEMENTS (yourcerts_columns); + cp->cert_type = E_CERT_USER; + cp->cert_filter_name = _("All PKCS12 files"); + cp->cert_mime_types = yourcerts_mime_types; + initialize_ui (cp); + + cp = g_new0 (CertPage, 1); + priv->contactcerts_page = cp; + cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "contactcerts-treeview")); + cp->streemodel = NULL; + cp->view_button = e_builder_get_widget (priv->builder, "contact-view-button"); + cp->backup_button = NULL; + cp->backup_all_button = NULL; + cp->edit_button = e_builder_get_widget (priv->builder, "contact-edit-button"); + cp->import_button = e_builder_get_widget (priv->builder, "contact-import-button"); + cp->delete_button = e_builder_get_widget (priv->builder, "contact-delete-button"); + cp->columns = contactcerts_columns; + cp->columns_count = G_N_ELEMENTS (contactcerts_columns); + cp->cert_type = E_CERT_CONTACT; + cp->cert_filter_name = _("All email certificate files"); + cp->cert_mime_types = contactcerts_mime_types; + initialize_ui (cp); + + cp = g_new0 (CertPage, 1); + priv->authoritycerts_page = cp; + cp->treeview = GTK_TREE_VIEW (e_builder_get_widget (priv->builder, "authoritycerts-treeview")); + cp->streemodel = NULL; + cp->view_button = e_builder_get_widget (priv->builder, "authority-view-button"); + cp->backup_button = NULL; + cp->backup_all_button = NULL; + cp->edit_button = e_builder_get_widget (priv->builder, "authority-edit-button"); + cp->import_button = e_builder_get_widget (priv->builder, "authority-import-button"); + cp->delete_button = e_builder_get_widget (priv->builder, "authority-delete-button"); + cp->columns = authoritycerts_columns; + cp->columns_count = G_N_ELEMENTS (authoritycerts_columns); + cp->cert_type = E_CERT_CA; + cp->cert_filter_name = _("All CA certificate files"); + cp->cert_mime_types = authoritycerts_mime_types; + initialize_ui (cp); + + /* Run this in an idle callback so Evolution has a chance to + * fully initialize itself and start its main loop before we + * load certificates, since doing so may trigger a password + * dialog, and dialogs require a main loop. + * Schedule with priority higher than gtk+ uses for animations + * (check docs for G_PRIORITY_HIGH_IDLE). */ + g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) populate_ui, ecmc, NULL); + + /* Disconnect cert-manager-notebook from it's window and attach it + * to this ECertManagerConfig */ + widget = e_builder_get_widget (priv->builder, "cert-manager-notebook"); + parent = gtk_widget_get_parent (widget); + gtk_container_remove (GTK_CONTAINER (parent), widget); + gtk_box_pack_start (GTK_BOX (ecmc), widget, TRUE, TRUE, 0); + gtk_widget_show_all (widget); + + /* FIXME: remove when implemented */ + gtk_widget_set_sensitive (priv->yourcerts_page->backup_button, FALSE); + gtk_widget_set_sensitive (priv->yourcerts_page->backup_all_button, FALSE); +} + +GtkWidget * +e_cert_manager_config_new (EPreferencesWindow *window) +{ + ECertManagerConfig *ecmc; + + ecmc = g_object_new (E_TYPE_CERT_MANAGER_CONFIG, "preferences-window", window, NULL); + + return GTK_WIDGET (ecmc); +} + +GtkWidget * +e_cert_manager_new_certificate_viewer (GtkWindow *parent, + ECert *cert) +{ + GtkWidget *dialog; + GList *chain, *citer; + GSList *issuers = NULL; + + g_return_val_if_fail (cert != NULL, NULL); + + chain = e_cert_get_issuers_chain (cert); + for (citer = chain; citer; citer = g_list_next (citer)) { + issuers = g_slist_append (issuers, e_cert_get_internal_cert (citer->data)); + } + + dialog = certificate_viewer_new ( + (GtkWindow *) parent, + e_cert_get_internal_cert (cert), + issuers); + + g_list_free_full (chain, g_object_unref); + g_slist_free (issuers); + + return dialog; +} diff --git a/smime/gui/certificate-manager.h b/smime/gui/certificate-manager.h new file mode 100644 index 0000000000..0b873f81da --- /dev/null +++ b/smime/gui/certificate-manager.h @@ -0,0 +1,73 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _CERTIFICATE_MANAGER_H_ +#define _CERTIFICATE_MANAGER_H + +#include <gtk/gtk.h> +#include <shell/e-shell.h> + +/* Standard GObject macros */ +#define E_TYPE_CERT_MANAGER_CONFIG \ + (e_cert_manager_config_get_type ()) +#define E_CERT_MANAGER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CERT_MANAGER_CONFIG, ECertManagerConfig)) +#define E_CERT_MANAGER_CONFIG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CERT_MANAGER_CONFIG, ECertManagerConfigClass)) +#define E_IS_CERT_MANAGER_CONFIG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CERT_MANAGER_CONFIG)) +#define E_IS_CERT_MANAGER_CONFIG_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CERT_MANAGER_CONFIG)) +#define E_CERT_MANAGER_CONFIG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CERT_MANAGER_CONFIG, ECertManagerConfigClass)) + +typedef struct _ECertManagerConfig ECertManagerConfig; +typedef struct _ECertManagerConfigClass ECertManagerConfigClass; +typedef struct _ECertManagerConfigPrivate ECertManagerConfigPrivate; + +struct _ECertManagerConfig { + GtkBox parent; + ECertManagerConfigPrivate *priv; +}; + +struct _ECertManagerConfigClass { + GtkBoxClass parent_class; +}; + +G_BEGIN_DECLS + +GType e_cert_manager_config_get_type (void) G_GNUC_CONST; + +GtkWidget *e_cert_manager_config_new (EPreferencesWindow *window); + +struct _ECert; /* forward declaration */ +GtkWidget *e_cert_manager_new_certificate_viewer (GtkWindow *parent, + struct _ECert *cert); + +G_END_DECLS + +#endif /* _CERTIFICATE_MANAGER_H_ */ diff --git a/smime/gui/certificate-viewer.c b/smime/gui/certificate-viewer.c new file mode 100644 index 0000000000..4fdd15e473 --- /dev/null +++ b/smime/gui/certificate-viewer.c @@ -0,0 +1,688 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "pk11pub.h" +#include "hasht.h" + +#include <libedataserver/libedataserver.h> + +#include "e-asn1-object.h" +#include "certificate-viewer.h" + +#define CERTIFICATE_VIEWER_PRIV_KEY "CertificateViewerPriv-key" + +typedef struct _CertificateViewerPriv +{ + GtkWidget *issued_to_cn; + GtkWidget *issued_to_o; + GtkWidget *issued_to_ou; + GtkWidget *issued_to_serial; + GtkWidget *issued_by_cn; + GtkWidget *issued_by_o; + GtkWidget *issued_by_ou; + GtkWidget *validity_issued_on; + GtkWidget *validity_expires_on; + GtkWidget *fingerprints_sha1; + GtkWidget *fingerprints_md5; + GtkWidget *cert_hierarchy_treeview; + GtkWidget *cert_fields_treeview; + GtkWidget *cert_field_value_textview; + + CERTCertificate *cert; + GSList *issuers; + GtkTextTag *monospace_tag; +} CertificateViewerPriv; + +static void +free_priv_struct (gpointer ptr) +{ + CertificateViewerPriv *priv = ptr; + GSList *iter; + + if (!priv) + return; + + if (priv->cert) + CERT_DestroyCertificate (priv->cert); + + for (iter = priv->issuers; iter; iter = iter->next) { + CERTCertificate *cert = iter->data; + + if (cert) + CERT_DestroyCertificate (cert); + } + + g_slist_free (priv->issuers); + + g_free (priv); +} + +static void +begin_section (GtkGrid *add_to, + const gchar *caption, + gint *from_row, + gint for_rows) +{ + GtkWidget *widget; + PangoAttribute *attr; + PangoAttrList *bold; + + g_return_if_fail (add_to != NULL); + g_return_if_fail (caption != NULL); + g_return_if_fail (from_row != NULL); + + bold = pango_attr_list_new (); + attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); + pango_attr_list_insert (bold, attr); + + widget = gtk_label_new (caption); + g_object_set ( + G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_START, + "justify", GTK_JUSTIFY_LEFT, + "attributes", bold, + "ellipsize", PANGO_ELLIPSIZE_NONE, + NULL); + + pango_attr_list_unref (bold); + + gtk_grid_attach (add_to, widget, 0, *from_row, 3, 1); + (*from_row)++; + + widget = gtk_alignment_new (0.0, 0.0, 0.0, 0.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0); + + gtk_grid_attach (add_to, widget, 0, *from_row, 1, for_rows); +} + +static GtkWidget * +add_info_label (GtkGrid *add_to, + const gchar *caption, + gint *at_row) +{ + GtkWidget *widget; + + g_return_val_if_fail (add_to != NULL, NULL); + g_return_val_if_fail (at_row != NULL, NULL); + + if (caption) { + widget = gtk_label_new (caption); + g_object_set ( + G_OBJECT (widget), + "hexpand", FALSE, + "halign", GTK_ALIGN_START, + "justify", GTK_JUSTIFY_LEFT, + "ellipsize", PANGO_ELLIPSIZE_NONE, + NULL); + + gtk_grid_attach (add_to, widget, 1, *at_row, 1, 1); + } + + widget = gtk_label_new (""); + g_object_set ( + G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_START, + "justify", GTK_JUSTIFY_LEFT, + "ellipsize", PANGO_ELLIPSIZE_NONE, + "selectable", caption != NULL, + NULL); + + gtk_grid_attach (add_to, widget, caption ? 2 : 1, *at_row, caption ? 1 : 2, 1); + + (*at_row)++; + + return widget; +} + +static GtkWidget * +add_scrolled_window (GtkGrid *add_to, + const gchar *caption, + gint *at_row, + GtkWidget *add_widget) +{ + GtkWidget *widget; + PangoAttribute *attr; + PangoAttrList *bold; + + g_return_val_if_fail (add_to != NULL, NULL); + g_return_val_if_fail (caption != NULL, NULL); + g_return_val_if_fail (at_row != NULL, NULL); + + bold = pango_attr_list_new (); + attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); + pango_attr_list_insert (bold, attr); + + widget = gtk_label_new (caption); + g_object_set ( + G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_START, + "justify", GTK_JUSTIFY_LEFT, + "attributes", bold, + "ellipsize", PANGO_ELLIPSIZE_NONE, + NULL); + + pango_attr_list_unref (bold); + + gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1); + (*at_row)++; + + widget = gtk_scrolled_window_new (NULL, NULL); + g_object_set ( + G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_FILL, + "vexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "hscrollbar-policy", GTK_POLICY_AUTOMATIC, + "vscrollbar-policy", GTK_POLICY_AUTOMATIC, + "shadow-type", GTK_SHADOW_ETCHED_IN, + NULL); + + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (widget), add_widget); + + gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1); + (*at_row)++; + + return add_widget; +} + +#define FLAG_NONE (0) +#define FLAG_PORT_MEMORY (1 << 0) +#define FLAG_MARKUP (1 << 1) + +static void +set_label_text (GtkWidget *label, + const gchar *not_part_markup, + gchar *text, + guint32 flags) +{ + if (text) { + if ((flags & FLAG_MARKUP) != 0) + gtk_label_set_markup (GTK_LABEL (label), text); + else + gtk_label_set_text (GTK_LABEL (label), text); + + if ((flags & FLAG_PORT_MEMORY) != 0) + PORT_Free (text); + else + g_free (text); + } else { + gtk_label_set_markup (GTK_LABEL (label), not_part_markup); + } +} + +static void +get_cert_times (CERTCertificate *cert, + gchar **issued_on, + gchar **expires_on) +{ + PRTime time_issued_on; + PRTime time_expires_on; + PRExplodedTime explodedTime; + struct tm exploded_tm; + gchar buf[128]; + + g_return_if_fail (cert != NULL); + g_return_if_fail (issued_on != NULL); + g_return_if_fail (expires_on != NULL); + + if (SECSuccess != CERT_GetCertTimes (cert, &time_issued_on, &time_expires_on)) + return; + + PR_ExplodeTime (time_issued_on, PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm); + *issued_on = g_strdup (buf); + + PR_ExplodeTime (time_expires_on, PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm); + *expires_on = g_strdup (buf); +} + +static void +fill_general_page (CertificateViewerPriv *priv) +{ + gchar *not_part_markup; + gchar *issued_on = NULL; + gchar *expires_on = NULL; + gchar *port_str; + guchar fingerprint[128]; + SECItem fpItem; + + g_return_if_fail (priv != NULL); + + not_part_markup = g_strconcat ("<i><", _("Not part of certificate"), "></i>", NULL); + + set_label_text (priv->issued_to_cn, not_part_markup, CERT_GetCommonName (&priv->cert->subject), FLAG_PORT_MEMORY); + set_label_text (priv->issued_to_o, not_part_markup, CERT_GetOrgName (&priv->cert->subject), FLAG_PORT_MEMORY); + set_label_text (priv->issued_to_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->subject), FLAG_PORT_MEMORY); + set_label_text (priv->issued_to_serial, not_part_markup, CERT_Hexify (&priv->cert->serialNumber, TRUE), FLAG_PORT_MEMORY); + + set_label_text (priv->issued_by_cn, not_part_markup, CERT_GetCommonName (&priv->cert->issuer), FLAG_PORT_MEMORY); + set_label_text (priv->issued_by_o, not_part_markup, CERT_GetOrgName (&priv->cert->issuer), FLAG_PORT_MEMORY); + set_label_text (priv->issued_by_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->issuer), FLAG_PORT_MEMORY); + + get_cert_times (priv->cert, &issued_on, &expires_on); + set_label_text (priv->validity_issued_on, not_part_markup, issued_on, FLAG_NONE); + set_label_text (priv->validity_expires_on, not_part_markup, expires_on, FLAG_NONE); + + memset (fingerprint, 0, sizeof fingerprint); + PK11_HashBuf ( + SEC_OID_SHA1, fingerprint, + priv->cert->derCert.data, + priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = SHA1_LENGTH; + port_str = CERT_Hexify (&fpItem, TRUE); + set_label_text (priv->fingerprints_sha1, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP); + PORT_Free (port_str); + + memset (fingerprint, 0, sizeof fingerprint); + PK11_HashBuf ( + SEC_OID_MD5, fingerprint, + priv->cert->derCert.data, + priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = MD5_LENGTH; + port_str = CERT_Hexify (&fpItem, TRUE); + set_label_text (priv->fingerprints_md5, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP); + PORT_Free (port_str); + + g_free (not_part_markup); +} + +static void +populate_fields_tree (CertificateViewerPriv *priv, + EASN1Object *asn1, + GtkTreeIter *root) +{ + GtkTreeStore *fields_store; + GtkTreeIter new_iter; + + if (!asn1) + return; + + fields_store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->cert_fields_treeview))); + + /* first insert a node for the current asn1 */ + gtk_tree_store_insert (fields_store, &new_iter, root, -1); + gtk_tree_store_set ( + fields_store, &new_iter, + 0, e_asn1_object_get_display_name (asn1), + 1, asn1, + -1); + + if (e_asn1_object_is_valid_container (asn1)) { + GList *children = e_asn1_object_get_children (asn1); + + if (children) { + GList *iter; + for (iter = children; iter; iter = iter->next) { + EASN1Object *subasn1 = iter->data; + + populate_fields_tree (priv, subasn1, &new_iter); + } + } + + g_list_free_full (children, g_object_unref); + } +} + +static void +hierarchy_selection_changed_cb (GtkTreeSelection *selection, + CertificateViewerPriv *priv) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + CERTCertificate *cert; + EASN1Object *asn1; + GtkTreeStore *fields_store; + + gtk_tree_model_get (model, &iter, 1, &cert, -1); + + if (!cert) + return; + + /* display the cert's ASN1 structure */ + asn1 = e_asn1_object_new_from_cert (cert); + + /* wipe out the old model */ + fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT); + gtk_tree_view_set_model ( + GTK_TREE_VIEW (priv->cert_fields_treeview), + GTK_TREE_MODEL (fields_store)); + + /* populate the fields from the newly selected cert */ + populate_fields_tree (priv, asn1, NULL); + gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_fields_treeview)); + if (asn1) + g_object_unref (asn1); + + /* and blow away the field value */ + gtk_text_buffer_set_text ( + gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)), + "", 0); + } +} + +static void +fields_selection_changed_cb (GtkTreeSelection *selection, + CertificateViewerPriv *priv) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + EASN1Object *asn1 = NULL; + const gchar *value = NULL; + GtkTextView *textview; + GtkTextBuffer *textbuffer; + + gtk_tree_model_get (model, &iter, 1, &asn1, -1); + + if (asn1) + value = e_asn1_object_get_display_value (asn1); + + textview = GTK_TEXT_VIEW (priv->cert_field_value_textview); + textbuffer = gtk_text_view_get_buffer (textview); + + gtk_text_buffer_set_text (textbuffer, "", 0); + + if (value) { + GtkTextIter text_iter; + + gtk_text_buffer_get_start_iter (textbuffer, &text_iter); + + gtk_text_buffer_insert_with_tags ( + textbuffer, &text_iter, + value, strlen (value), + priv->monospace_tag, NULL); + } + + if (asn1) + g_object_unref (asn1); + } +} + +static void +fill_details_page (CertificateViewerPriv *priv) +{ + GSList *iter; + GtkTreeIter root; + GtkTreeSelection *selection; + gboolean root_set = FALSE; + GtkTreeStore *hierarchy_store; + + g_return_if_fail (priv != NULL); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_hierarchy_treeview), FALSE); + + hierarchy_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); + gtk_tree_view_set_model ( + GTK_TREE_VIEW (priv->cert_hierarchy_treeview), + GTK_TREE_MODEL (hierarchy_store)); + + gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (priv->cert_hierarchy_treeview), + -1, "Cert", gtk_cell_renderer_text_new (), + "text", 0, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_hierarchy_treeview)); + g_signal_connect ( + selection, "changed", + G_CALLBACK (hierarchy_selection_changed_cb), priv); + + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_fields_treeview), FALSE); + + gtk_tree_view_insert_column_with_attributes ( + GTK_TREE_VIEW (priv->cert_fields_treeview), + -1, "Field", gtk_cell_renderer_text_new (), + "text", 0, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_fields_treeview)); + g_signal_connect ( + selection, "changed", + G_CALLBACK (fields_selection_changed_cb), priv); + + /* set the font of the field value viewer to be some fixed + * width font to the hex display looks nice. */ + priv->monospace_tag = gtk_text_buffer_create_tag ( + gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)), + "mono", "font", "Mono", NULL); + + /* initially populate the hierarchy from the issuers' chain */ + for (iter = priv->issuers; iter; iter = g_slist_next (iter)) { + CERTCertificate *cert = iter->data; + gchar *str; + GtkTreeIter new_iter; + + if (!cert) + continue; + + str = CERT_GetCommonName (&cert->subject); + + gtk_tree_store_insert (hierarchy_store, &new_iter, root_set ? &root : NULL, -1); + gtk_tree_store_set ( + hierarchy_store, &new_iter, + 0, str ? str : cert->subjectName, + 1, cert, + -1); + + root = new_iter; + root_set = TRUE; + + if (str) + PORT_Free (str); + } + + gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_hierarchy_treeview)); +} + +static gchar * +get_window_title (CERTCertificate *cert) +{ + gchar *str; + + g_return_val_if_fail (cert != NULL, NULL); + + if (cert->nickname) + return g_strdup (cert->nickname); + + str = CERT_GetCommonName (&cert->subject); + if (str) { + gchar *title; + + title = g_strdup (str); + PORT_Free (str); + + return title; + } + + return cert->subjectName; +} + +GtkWidget * +certificate_viewer_new (GtkWindow *parent, + const CERTCertificate *cert, + const GSList *issuers_chain_certs) +{ + CertificateViewerPriv *priv; + GtkWidget *dialog, *notebook, *widget; + GtkGrid *grid; + gint row; + GSList *iter; + gchar *title; + + g_return_val_if_fail (cert != NULL, NULL); + + priv = g_new0 (CertificateViewerPriv, 1); + priv->cert = CERT_DupCertificate ((CERTCertificate *) cert); + priv->issuers = g_slist_copy ((GSList *) issuers_chain_certs); + + /* root issuer first, then bottom down to certificate itself */ + priv->issuers = g_slist_reverse (priv->issuers); + priv->issuers = g_slist_append (priv->issuers, priv->cert); + + for (iter = priv->issuers; iter; iter = g_slist_next (iter)) { + iter->data = CERT_DupCertificate (iter->data); + } + + title = get_window_title (priv->cert); + + dialog = gtk_dialog_new_with_buttons ( + title, parent, + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + g_free (title); + + g_object_set_data_full (G_OBJECT (dialog), CERTIFICATE_VIEWER_PRIV_KEY, priv, free_priv_struct); + + notebook = gtk_notebook_new (); + gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), notebook); + gtk_container_set_border_width (GTK_CONTAINER (notebook), 12); + + /* General page */ + row = 0; + grid = GTK_GRID (gtk_grid_new ()); + g_object_set ( + G_OBJECT (grid), + "hexpand", TRUE, + "halign", GTK_ALIGN_FILL, + "vexpand", FALSE, + "valign", GTK_ALIGN_START, + "border-width", 12, + "row-spacing", 6, + "column-spacing", 6, + NULL); + + begin_section (grid, _("This certificate has been verified for the following uses:"), &row, 4); + + if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageSSLClient) != 0) { + widget = add_info_label (grid, NULL, &row); + gtk_label_set_text (GTK_LABEL (widget), _("SSL Client Certificate")); + } + + if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & (certificateUsageSSLServer | certificateUsageSSLCA)) != 0) { + widget = add_info_label (grid, NULL, &row); + gtk_label_set_text (GTK_LABEL (widget), _("SSL Server Certificate")); + } + + if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailSigner) != 0) { + widget = add_info_label (grid, NULL, &row); + gtk_label_set_text (GTK_LABEL (widget), _("Email Signer Certificate")); + } + + if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailRecipient) != 0) { + widget = add_info_label (grid, NULL, &row); + gtk_label_set_text (GTK_LABEL (widget), _("Email Recipient Certificate")); + } + + widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + g_object_set ( + G_OBJECT (widget), + "hexpand", TRUE, + "halign", GTK_ALIGN_FILL, + "vexpand", FALSE, + "valign", GTK_ALIGN_START, + NULL); + + gtk_grid_attach (grid, widget, 0, row, 3, 1); + row++; + + begin_section (grid, _("Issued To"), &row, 4); + priv->issued_to_cn = add_info_label (grid, _("Common Name (CN)"), &row); + priv->issued_to_o = add_info_label (grid, _("Organization (O)"), &row); + priv->issued_to_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row); + priv->issued_to_serial = add_info_label (grid, _("Serial Number"), &row); + + begin_section (grid, _("Issued By"), &row, 3); + priv->issued_by_cn = add_info_label (grid, _("Common Name (CN)"), &row); + priv->issued_by_o = add_info_label (grid, _("Organization (O)"), &row); + priv->issued_by_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row); + + begin_section (grid, _("Validity"), &row, 2); + priv->validity_issued_on = add_info_label (grid, _("Issued On"), &row); + priv->validity_expires_on = add_info_label (grid, _("Expires On"), &row); + + begin_section (grid, _("Fingerprints"), &row, 2); + priv->fingerprints_sha1 = add_info_label (grid, _("SHA1 Fingerprint"), &row); + priv->fingerprints_md5 = add_info_label (grid, _("MD5 Fingerprint"), &row); + + widget = gtk_label_new (_("General")); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget); + + /* Details page */ + row = 0; + grid = GTK_GRID (gtk_grid_new ()); + g_object_set ( + G_OBJECT (grid), + "hexpand", TRUE, + "halign", GTK_ALIGN_FILL, + "vexpand", TRUE, + "valign", GTK_ALIGN_FILL, + "border-width", 12, + "row-spacing", 6, + "column-spacing", 6, + NULL); + + priv->cert_hierarchy_treeview = add_scrolled_window ( + grid, _("Certificate Hierarchy"), &row, gtk_tree_view_new ()); + + priv->cert_fields_treeview = add_scrolled_window ( + grid, _("Certificate Fields"), &row, gtk_tree_view_new ()); + + priv->cert_field_value_textview = add_scrolled_window ( + grid, _("Field Value"), &row, gtk_text_view_new ()); + + widget = gtk_label_new (_("Details")); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget); + + gtk_widget_show_all (notebook); + + fill_general_page (priv); + fill_details_page (priv); + + return dialog; +} diff --git a/smime/gui/certificate-viewer.h b/smime/gui/certificate-viewer.h new file mode 100644 index 0000000000..41f2c83c7b --- /dev/null +++ b/smime/gui/certificate-viewer.h @@ -0,0 +1,32 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifndef CERTIFICATE_VIEWER_H +#define CERTIFICATE_VIEWER_H + +#include <gtk/gtk.h> +#include <cert.h> + +GtkWidget * certificate_viewer_new (GtkWindow *parent, + const CERTCertificate *cert, + const GSList *issuers_chain_certs); + +#endif /* CERTIFICATE_VIEWER_H */ diff --git a/smime/gui/component.c b/smime/gui/component.c new file mode 100644 index 0000000000..790bb68ff5 --- /dev/null +++ b/smime/gui/component.c @@ -0,0 +1,141 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "component.h" + +#include <gtk/gtk.h> + +#include <glib/gi18n.h> + +#include "e-util/e-util.h" + +#include "ca-trust-dialog.h" +#include "e-cert-db.h" +#include "pk11func.h" + +static gboolean +smime_pk11_passwd (ECertDB *db, + PK11SlotInfo *slot, + gboolean retry, + gchar **passwd, + gpointer arg) +{ + gchar *prompt; + gchar *slot_name = g_strdup (PK11_GetSlotName (slot)); + gchar *token_name = g_strdup (PK11_GetTokenName (slot)); + + g_strchomp (slot_name); + + if (token_name) + g_strchomp (token_name); + + if (token_name && *token_name && g_ascii_strcasecmp (slot_name, token_name) != 0) + prompt = g_strdup_printf (_("Enter the password for '%s', token '%s'"), slot_name, token_name); + else + prompt = g_strdup_printf (_("Enter the password for '%s'"), slot_name); + + g_free (slot_name); + g_free (token_name); + + *passwd = e_passwords_ask_password ( + _("Enter password"), "", prompt, + E_PASSWORDS_REMEMBER_NEVER | E_PASSWORDS_SECRET, + NULL, NULL); + + g_free (prompt); + + /* this should return FALSE if they canceled. */ + return TRUE; +} + +static gboolean +smime_pk11_change_passwd (ECertDB *db, + gchar **old_passwd, + gchar **passwd, + gpointer arg) +{ + gchar *prompt; + + /* XXX need better strings here, just copy mozilla's? */ + + if (!old_passwd) { + /* we're setting the password initially */ + prompt = _("Enter new password for certificate database"); + + *passwd = e_passwords_ask_password ( + _("Enter new password"), "", prompt, + E_PASSWORDS_REMEMBER_NEVER | E_PASSWORDS_SECRET, + NULL, NULL); + } + else { + /* we're changing the password */ + /* XXX implement this... */ + } + + /* this should return FALSE if they canceled. */ + return TRUE; +} + +static gboolean +smime_confirm_ca_cert_import (ECertDB *db, + ECert *cert, + gboolean *trust_ssl, + gboolean *trust_email, + gboolean *trust_objsign, + gpointer arg) +{ + GtkWidget *dialog = ca_trust_dialog_show (cert, TRUE); + gint response; + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + ca_trust_dialog_get_trust (dialog, trust_ssl, trust_email, trust_objsign); + + gtk_widget_destroy (dialog); + + return response != GTK_RESPONSE_CANCEL; +} + +void +smime_component_init (void) +{ + static gboolean init_done = FALSE; + if (init_done) + return; + + init_done = TRUE; + g_signal_connect ( + e_cert_db_peek (), "pk11_passwd", + G_CALLBACK (smime_pk11_passwd), NULL); + + g_signal_connect ( + e_cert_db_peek (), "pk11_change_passwd", + G_CALLBACK (smime_pk11_change_passwd), NULL); + + g_signal_connect ( + e_cert_db_peek (), "confirm_ca_cert_import", + G_CALLBACK (smime_confirm_ca_cert_import), NULL); +} diff --git a/smime/gui/component.h b/smime/gui/component.h new file mode 100644 index 0000000000..a41c4d1209 --- /dev/null +++ b/smime/gui/component.h @@ -0,0 +1,28 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _SMIME_COMPONENT_H_ +#define _SMIME_COMPONENT_H_ + +void smime_component_init (void); + +#endif /* _SMIME_COMPONENT_H_ */ diff --git a/smime/gui/e-cert-selector.c b/smime/gui/e-cert-selector.c new file mode 100644 index 0000000000..2928b550ab --- /dev/null +++ b/smime/gui/e-cert-selector.c @@ -0,0 +1,277 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "nss.h" +#include "pk11func.h" +#include "certdb.h" +#include "cert.h" + +#include "e-cert-selector.h" + +#include "e-util/e-util.h" +#include "e-util/e-util-private.h" + +#define E_CERT_SELECTOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CERT_SELECTOR, ECertSelectorPrivate)) + +struct _ECertSelectorPrivate { + CERTCertList *certlist; + + GtkWidget *combobox, *description; +}; + +enum { + ECS_SELECTED, + ECS_LAST_SIGNAL +}; + +static guint ecs_signals[ECS_LAST_SIGNAL]; + +G_DEFINE_TYPE (ECertSelector, e_cert_selector, GTK_TYPE_DIALOG) + +/* (this is what mozilla shows) + * Issued to: + * Subject: E=notzed@ximian.com, CN=notzed@ximian.com, O=My Company Ltd, L=Adelaide, ST=SA, C=AU + * Serial Number: 03 + * Valid from 23/10/03 06:35:29 to 22/10/04 06:35:29 + * Purposes: Sign,Encrypt + * Issued by: + * Subject: E=notzed@ximian.com, O=company, L=there, ST=Here, C=AU + */ + +static CERTCertListNode * +ecs_find_current (ECertSelector *ecs) +{ + struct _ECertSelectorPrivate *p = ecs->priv; + CERTCertListNode *node; + gint n; + + if (p->certlist == NULL || CERT_LIST_EMPTY (p->certlist)) + return NULL; + + n = gtk_combo_box_get_active (GTK_COMBO_BOX (p->combobox)); + node = CERT_LIST_HEAD (p->certlist); + while (n > 0 && !CERT_LIST_END (node, p->certlist)) { + n--; + node = CERT_LIST_NEXT (node); + } + + g_return_val_if_fail (!CERT_LIST_END (node, p->certlist), NULL); + + return node; +} + +static void +e_cert_selector_response (GtkDialog *dialog, + gint button) +{ + CERTCertListNode *node; + + switch (button) { + case GTK_RESPONSE_OK: + node = ecs_find_current ((ECertSelector *) dialog); + break; + default: + node = NULL; + break; + } + + g_signal_emit (dialog, ecs_signals[ECS_SELECTED], 0, node ? node->cert->nickname : NULL); +} + +static void +ecs_cert_changed (GtkWidget *w, + ECertSelector *ecs) +{ + struct _ECertSelectorPrivate *p = ecs->priv; + CERTCertListNode *node; + GtkTextBuffer *buffer; + GString *text; + + text = g_string_new (""); + node = ecs_find_current (ecs); + if (node) { + /* FIXME: add serial no, validity date, uses */ + g_string_append_printf (text, _("Issued to:\n Subject: %s\n"), node->cert->subjectName); + g_string_append_printf (text, _("Issued by:\n Subject: %s\n"), node->cert->issuerName); + } + + buffer = gtk_text_view_get_buffer ((GtkTextView *) p->description); + gtk_text_buffer_set_text (buffer, text->str, text->len); + g_string_free (text, TRUE); +} + +/** + * e_cert_selector_new: + * @type: + * @currentid: + * + * Create a new ECertSelector dialog. @type specifies which type of cert to + * be selected, E_CERT_SELECTOR_SIGNER for signing certs, and + * E_CERT_SELECTOR_RECIPIENT for encrypting certs. + * + * @currentid is the nickname of the cert currently selected for this user. + * + * You only need to connect to a single signal "selected" which will + * be called with either a NULL nickname if cancelled, or the newly + * selected nickname otherwise. + * + * Return value: A dialogue to be shown. + **/ +GtkWidget * +e_cert_selector_new (gint type, + const gchar *currentid) +{ + ECertSelector *ecs; + struct _ECertSelectorPrivate *p; + SECCertUsage usage; + CERTCertList *certlist; + CERTCertListNode *node; + GtkBuilder *builder; + GtkWidget *content_area; + GtkWidget *w; + GtkListStore *store; + GtkTreeIter iter; + gint n = 0, active = 0; + + ecs = g_object_new (e_cert_selector_get_type (), NULL); + p = ecs->priv; + + builder = gtk_builder_new (); + e_load_ui_builder_definition (builder, "smime-ui.ui"); + + p->combobox = e_builder_get_widget (builder, "cert_combobox"); + p->description = e_builder_get_widget (builder, "cert_description"); + + w = e_builder_get_widget (builder, "cert_selector_vbox"); + content_area = gtk_dialog_get_content_area (GTK_DIALOG (ecs)); + gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 3); + gtk_window_set_title (GTK_WINDOW (ecs), _("Select certificate")); + + switch (type) { + case E_CERT_SELECTOR_SIGNER: + default: + usage = certUsageEmailSigner; + break; + case E_CERT_SELECTOR_RECIPIENT: + usage = certUsageEmailRecipient; + break; + } + + store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (p->combobox))); + gtk_list_store_clear (store); + + certlist = CERT_FindUserCertsByUsage (CERT_GetDefaultCertDB (), usage, FALSE, TRUE, NULL); + ecs->priv->certlist = certlist; + if (certlist != NULL) { + node = CERT_LIST_HEAD (certlist); + while (!CERT_LIST_END (node, certlist)) { + if (node->cert->nickname || node->cert->emailAddr) { + gtk_list_store_append (store, &iter); + gtk_list_store_set ( + store, &iter, + 0, node->cert->nickname ? node->cert->nickname : node->cert->emailAddr, + -1); + + if (currentid != NULL + && ((node->cert->nickname != NULL && strcmp (node->cert->nickname, currentid) == 0) + || (node->cert->emailAddr != NULL && strcmp (node->cert->emailAddr, currentid) == 0))) + active = n; + + n++; + } + + node = CERT_LIST_NEXT (node); + } + } + + gtk_combo_box_set_active (GTK_COMBO_BOX (p->combobox), active); + + g_signal_connect ( + p->combobox, "changed", + G_CALLBACK (ecs_cert_changed), ecs); + + g_object_unref (builder); + + ecs_cert_changed (p->combobox, ecs); + + return GTK_WIDGET (ecs); +} + +static void +e_cert_selector_init (ECertSelector *ecs) +{ + gtk_dialog_add_buttons ( + GTK_DIALOG (ecs), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + ecs->priv = E_CERT_SELECTOR_GET_PRIVATE (ecs); +} + +static void +e_cert_selector_finalize (GObject *object) +{ + ECertSelectorPrivate *priv; + + priv = E_CERT_SELECTOR_GET_PRIVATE (object); + + if (priv->certlist) + CERT_DestroyCertList (priv->certlist); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_cert_selector_parent_class)->finalize (object); +} + +static void +e_cert_selector_class_init (ECertSelectorClass *class) +{ + GObjectClass *object_class; + GtkDialogClass *dialog_class; + + g_type_class_add_private (class, sizeof (ECertSelectorPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_cert_selector_finalize; + + dialog_class = GTK_DIALOG_CLASS (class); + dialog_class->response = e_cert_selector_response; + + ecs_signals[ECS_SELECTED] = g_signal_new ( + "selected", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertSelectorClass, selected), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); +} + diff --git a/smime/gui/e-cert-selector.h b/smime/gui/e-cert-selector.h new file mode 100644 index 0000000000..e51f1263ef --- /dev/null +++ b/smime/gui/e-cert-selector.h @@ -0,0 +1,76 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Michael Zucchi <notzed@novell.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef E_CERT_SELECTOR_H +#define E_CERT_SELECTOR_H + +#include <gtk/gtk.h> + +/* Standard GObject macros */ +#define E_TYPE_CERT_SELECTOR \ + (e_cert_selector_get_type ()) +#define E_CERT_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_CERT_SELECTOR, ECertSelector)) +#define E_CERT_SELECTOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_CERT_SELECTOR, ECertSelectorClass)) +#define E_IS_CERT_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_CERT_SELECTOR)) +#define E_IS_CERT_SELECTOR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_CERT_SELECTOR)) +#define E_CERT_SELECTOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_CERT_SELECTOR, ECertSelectorClass)) + +G_BEGIN_DECLS + +typedef struct _ECertSelector ECertSelector; +typedef struct _ECertSelectorClass ECertSelectorClass; +typedef struct _ECertSelectorPrivate ECertSelectorPrivate; + +struct _ECertSelector { + GtkDialog parent; + ECertSelectorPrivate *priv; +}; + +struct _ECertSelectorClass { + GtkDialogClass parent_class; + + void (*selected)(ECertSelector *, const gchar *certid); +}; + +enum _e_cert_selector_type { + E_CERT_SELECTOR_SIGNER, + E_CERT_SELECTOR_RECIPIENT +}; + +GType e_cert_selector_get_type (void); +GtkWidget *e_cert_selector_new (gint type, const gchar *currentid); + +G_END_DECLS + +#endif /* E_CERT_SELECTOR_H */ + diff --git a/smime/gui/smime-ui.ui b/smime/gui/smime-ui.ui new file mode 100644 index 0000000000..5622c4d0e5 --- /dev/null +++ b/smime/gui/smime-ui.ui @@ -0,0 +1,799 @@ +<?xml version="1.0"?> +<interface> + <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy toplevel-contextual --> + <object class="GtkListStore" id="model1"> + <columns> + <!-- column-name gchararray --> + <column type="gchararray"/> + </columns> + <data> + <row> + <col id="0" translatable="yes">a</col> + </row> + <row> + <col id="0" translatable="yes">b</col> + </row> + </data> + </object> + <object class="GtkWindow" id="cert-manager-config-control"> + <property name="title">window1</property> + <child> + <object class="GtkNotebook" id="cert-manager-notebook"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkVBox" id="vbox4"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label57"> + <property name="visible">True</property> + <property name="label" translatable="yes">You have certificates from these organizations that identify you:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox6"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow4"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="yourcerts-treeview"> + <property name="name">yourcerts</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="yourcerts-treeview-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes">Certificates Table</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVButtonBox" id="vbuttonbox1"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="layout_style">start</property> + <child> + <object class="GtkButton" id="your-view-button"> + <property name="label" translatable="yes">_View</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="your-backup-button"> + <property name="label" translatable="yes" comments="This is a verb, as in "make a backup".">_Backup</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="image">backup-image</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="your-backup-all-button"> + <property name="label" translatable="yes">Backup _All</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="image">backup-all-image</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="your-import-button"> + <property name="label" translatable="yes">I_mport</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkButton" id="your-delete-button"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">4</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <child type="tab"> + <object class="GtkLabel" id="label54"> + <property name="visible">True</property> + <property name="label" translatable="yes">Your Certificates</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox5"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label60"> + <property name="visible">True</property> + <property name="label" translatable="yes">You have certificates on file that identify these people:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox7"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow5"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="contactcerts-treeview"> + <property name="name">contactcerts</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="contactcerts-treeview-atkobject"> + <property name="AtkObject::accessible-name" translatable="yes">Certificates Table</property> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVButtonBox" id="vbuttonbox2"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="layout_style">start</property> + <child> + <object class="GtkButton" id="contact-view-button"> + <property name="label" translatable="yes">_View</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="contact-edit-button"> + <property name="label">gtk-edit</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="contact-import-button"> + <property name="label" translatable="yes">I_mport</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="contact-delete-button"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="label55"> + <property name="visible">True</property> + <property name="label" translatable="yes">Contact Certificates</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox6"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label62"> + <property name="visible">True</property> + <property name="label" translatable="yes">You have certificates on file that identify these certificate authorities:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox8"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow6"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="authoritycerts-treeview"> + <property name="name">authoritycerts</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVButtonBox" id="vbuttonbox3"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="layout_style">start</property> + <child> + <object class="GtkButton" id="authority-view-button"> + <property name="label" translatable="yes">_View</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="authority-edit-button"> + <property name="label">gtk-edit</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="authority-import-button"> + <property name="label" translatable="yes">I_mport</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="authority-delete-button"> + <property name="label">gtk-delete</property> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="position">2</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="label56"> + <property name="visible">True</property> + <property name="label" translatable="yes">Authorities</property> + </object> + <packing> + <property name="position">2</property> + <property name="tab_fill">False</property> + </packing> + </child> + </object> + </child> + </object> + <object class="GtkDialog" id="ca-trust-dialog"> + <property name="title" translatable="yes">Certificate Authority Trust</property> + <property name="window_position">center</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox2"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkVBox" id="vbox9"> + <property name="visible">True</property> + <property name="border_width">12</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkLabel" id="ca-trust-label"> + <property name="visible">True</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="ssl_trust_checkbutton"> + <property name="label" translatable="yes">Trust this CA to identify _websites.</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="email_trust_checkbutton"> + <property name="label" translatable="yes">Trust this CA to identify _email users.</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="objsign_trust_checkbutton"> + <property name="label" translatable="yes">Trust this CA to identify _software developers.</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label66"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Before trusting this CA for any purpose, you should examine its certificate and its policy and procedures (if available).</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">4</property> + </packing> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area2"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="view_certificate_button"> + <property name="label" translatable="yes">_View Certificate</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancelbutton"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="okbutton"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-3">view_certificate_button</action-widget> + <action-widget response="-6">cancelbutton</action-widget> + <action-widget response="-5">okbutton</action-widget> + </action-widgets> + </object> + <object class="GtkVBox" id="cert_selector_vbox"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkHBox" id="hbox5"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label67"> + <property name="visible">True</property> + <property name="label" translatable="yes">Certificate</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkComboBox" id="cert_combobox"> + <property name="visible">True</property> + <property name="model">model1</property> + <child> + <object class="GtkCellRendererText" id="renderer1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkFrame" id="frame7"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow7"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="border_width">6</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTextView" id="cert_description"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + </child> + </object> + </child> + <child type="label"> + <object class="GtkLabel" id="label68"> + <property name="visible">True</property> + <property name="label" translatable="yes">Certificate details</property> + </object> + </child> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </object> + <object class="GtkDialog" id="cert-trust-dialog"> + <property name="title" translatable="yes">Email Certificate Trust Settings</property> + <property name="window_position">center</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkVBox" id="vbox8"> + <property name="visible">True</property> + <property name="border_width">12</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel" id="trust-label"> + <property name="visible">True</property> + <property name="wrap">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="cert-trust"> + <property name="label" translatable="yes">Trust the authenticity of this certificate</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="cert-notrust"> + <property name="label" translatable="yes">Do not trust the authenticity of this certificate</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">cert-trust</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkHButtonBox" id="dialog-action_area3"> + <property name="visible">True</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="editca"> + <property name="label" translatable="yes">_Edit CA Trust</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="editok"> + <property name="label">gtk-ok</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-3">editca</action-widget> + <action-widget response="-5">editok</action-widget> + </action-widgets> + </object> + <object class="GtkImage" id="backup-image"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + </object> + <object class="GtkImage" id="backup-all-image"> + <property name="visible">True</property> + <property name="stock">gtk-save</property> + </object> +</interface> diff --git a/smime/lib/Makefile.am b/smime/lib/Makefile.am new file mode 100644 index 0000000000..a8e2bfbbba --- /dev/null +++ b/smime/lib/Makefile.am @@ -0,0 +1,41 @@ +privsolib_LTLIBRARIES = libessmime.la + +libessmime_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DG_LOG_DOMAIN=\"evolution-smime\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/shell \ + -I$(top_builddir) \ + -DEVOLUTION_DATADIR=\""$(datadir)"\" \ + -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ + -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ + -DEVOLUTION_LOCALEDIR=\""$(localedir)"\" \ + -DEVOLUTION_UIDIR=\""$(uidir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(GTKHTML_CFLAGS) \ + $(CERT_UI_CFLAGS) + +libessmime_la_SOURCES = \ + e-asn1-object.c \ + e-asn1-object.h \ + e-cert.c \ + e-cert.h \ + e-cert-trust.c \ + e-cert-trust.h \ + e-cert-db.c \ + e-cert-db.h \ + e-pkcs12.c \ + e-pkcs12.h + +libessmime_la_LIBADD = \ + $(top_builddir)/e-util/libevolution-util.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) \ + $(GTKHTML_LIBS) \ + $(CERT_UI_LIBS) + +libessmime_la_LDFLAGS = -avoid-version $(NO_UNDEFINED) + +-include $(top_srcdir)/git.mk diff --git a/smime/lib/e-asn1-object.c b/smime/lib/e-asn1-object.c new file mode 100644 index 0000000000..90411d0395 --- /dev/null +++ b/smime/lib/e-asn1-object.c @@ -0,0 +1,964 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gi18n.h> + +#include "e-asn1-object.h" + +#include "pk11func.h" +#include "certdb.h" +#include "hasht.h" + +#define E_ASN1_OBJECT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ASN1_OBJECT, EASN1ObjectPrivate)) + +struct _EASN1ObjectPrivate { + PRUint32 tag; + PRUint32 type; + gboolean valid_container; + + GList *children; + + gchar *display_name; + gchar *value; + + gchar *data; + guint data_len; +}; + +G_DEFINE_TYPE (EASN1Object, e_asn1_object, G_TYPE_OBJECT) + +static gboolean +get_int_value (SECItem *versionItem, + gulong *version) +{ + SECStatus srv; + srv = SEC_ASN1DecodeInteger (versionItem,version); + if (srv != SECSuccess) { + g_warning ("could not decode version of cert"); + return FALSE; + } + return TRUE; +} + +static gboolean +process_version (SECItem *versionItem, + EASN1Object **retItem) +{ + EASN1Object *item = e_asn1_object_new (); + gulong version; + + e_asn1_object_set_display_name (item, _("Version")); + + /* Now to figure out what version this certificate is. */ + + if (versionItem->data) { + if (!get_int_value (versionItem, &version)) + return FALSE; + } else { + /* If there is no version present in the cert, then rfc2459 + * says we default to v1 (0) */ + version = 0; + } + + switch (version) { + case 0: + e_asn1_object_set_display_value (item, _("Version 1")); + break; + case 1: + e_asn1_object_set_display_value (item, _("Version 2")); + break; + case 2: + e_asn1_object_set_display_value (item, _("Version 3")); + break; + default: + g_warning ("Bad value for cert version"); + return FALSE; + } + + *retItem = item; + return TRUE; +} + +static gboolean +process_serial_number_der (SECItem *serialItem, + EASN1Object **retItem) +{ + gchar *serialNumber; + EASN1Object *item = e_asn1_object_new (); + + e_asn1_object_set_display_name (item, _("Serial Number")); + + serialNumber = CERT_Hexify (serialItem, 1); + + e_asn1_object_set_display_value (item, serialNumber); + PORT_Free (serialNumber); /* XXX the right free to use? */ + + *retItem = item; + return TRUE; +} + +static gboolean +get_default_oid_format (SECItem *oid, + gchar **text) +{ + GString *str; + gulong val = oid->data[0]; + guint ii = val % 40; + + val /= 40; + + str = g_string_new (""); + g_string_append_printf (str, "%lu %u ", val, ii); + + val = 0; + for (ii = 1; ii < oid->len; ii++) { + /* In this loop, we have to parse a DER formatted + * If the first bit is a 1, then the integer is + * represented by more than one byte. If the + * first bit is set then we continue on and add + * the values of the later bytes until we get + * a byte without the first bit set. + */ + gulong jj; + + jj = oid->data[ii]; + val = (val << 7) | (jj & 0x7f); + if (jj & 0x80) + continue; + g_string_append_printf (str, "%lu ", val); + + val = 0; + } + + *text = g_string_free (str, FALSE); + + return TRUE; +} + +static gboolean +get_oid_text (SECItem *oid, + gchar **text) +{ + SECOidTag oidTag = SECOID_FindOIDTag (oid); + gchar *temp; + + switch (oidTag) { + case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD2 With RSA Encryption")); + break; + case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 MD5 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-1 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-256 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-384 With RSA Encryption")); + break; + case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 SHA-512 With RSA Encryption")); + break; + case SEC_OID_AVA_COUNTRY_NAME: + *text = g_strdup ("C"); + break; + case SEC_OID_AVA_COMMON_NAME: + *text = g_strdup ("CN"); + break; + case SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME: + *text = g_strdup ("OU"); + break; + case SEC_OID_AVA_ORGANIZATION_NAME: + *text = g_strdup ("O"); + break; + case SEC_OID_AVA_LOCALITY: + *text = g_strdup ("L"); + break; + case SEC_OID_AVA_DN_QUALIFIER: + *text = g_strdup ("DN"); + break; + case SEC_OID_AVA_DC: + *text = g_strdup ("DC"); + break; + case SEC_OID_AVA_STATE_OR_PROVINCE: + *text = g_strdup ("ST"); + break; + case SEC_OID_PKCS1_RSA_ENCRYPTION: + *text = g_strdup (_("PKCS #1 RSA Encryption")); + break; + case SEC_OID_X509_KEY_USAGE: + *text = g_strdup (_("Certificate Key Usage")); + break; + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + *text = g_strdup (_("Netscape Certificate Type")); + break; + case SEC_OID_X509_AUTH_KEY_ID: + *text = g_strdup (_("Certificate Authority Key Identifier")); + break; + case SEC_OID_RFC1274_UID: + *text = g_strdup ("UID"); + break; + case SEC_OID_PKCS9_EMAIL_ADDRESS: + *text = g_strdup ("E"); + break; + default: + if (!get_default_oid_format (oid, &temp)) + return FALSE; + + *text = g_strdup_printf (_("Object Identifier (%s)"), temp); + g_free (temp); + + break; + } + return TRUE; +} + +static gboolean +process_raw_bytes (SECItem *data, + gchar **text) +{ + /* This function is used to display some DER bytes + * that we have not added support for decoding. + * It prints the value of the byte out into a + * string that can later be displayed as a byte + * string. We place a new line after 24 bytes + * to break up extermaly long sequence of bytes. + */ + GString *str = g_string_new (""); + PRUint32 i; + + for (i = 0; i < data->len; i++) { + g_string_append_printf (str, "%02x ", data->data[i]); + if ((i + 1) % 16 == 0) { + g_string_append (str, "\n"); + } + } + *text = g_string_free (str, FALSE); + return TRUE; +} + +static gboolean +process_sec_algorithm_id (SECAlgorithmID *algID, + EASN1Object **retSequence) +{ + EASN1Object *sequence = e_asn1_object_new (); + gchar *text = NULL; + + *retSequence = NULL; + + get_oid_text (&algID->algorithm, &text); + + if (!algID->parameters.len || + algID->parameters.data[0] == E_ASN1_OBJECT_TYPE_NULL) { + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_set_valid_container (sequence, FALSE); + } else { + EASN1Object *subitem; + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Identifier")); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + g_free (text); + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Algorithm Parameters")); + process_raw_bytes (&algID->parameters, &text); + e_asn1_object_set_display_value (subitem, text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + } + + g_free (text); + *retSequence = sequence; + return TRUE; +} + +static gboolean +process_subject_public_key_info (CERTSubjectPublicKeyInfo *spki, + EASN1Object *parentSequence) +{ + EASN1Object *spkiSequence = e_asn1_object_new (); + EASN1Object *sequenceItem; + EASN1Object *printableItem; + SECItem data; + gchar *text = NULL; + + e_asn1_object_set_display_name (spkiSequence, _("Subject Public Key Info")); + + if (!process_sec_algorithm_id (&spki->algorithm, &sequenceItem)) + return FALSE; + + e_asn1_object_set_display_name (sequenceItem, _("Subject Public Key Algorithm")); + + e_asn1_object_append_child (spkiSequence, sequenceItem); + + /* The subjectPublicKey field is encoded as a bit string. + * ProcessRawBytes expects the lenght to be in bytes, so + * let's convert the lenght into a temporary SECItem. + */ + data.data = spki->subjectPublicKey.data; + data.len = spki->subjectPublicKey.len / 8; + + process_raw_bytes (&data, &text); + printableItem = e_asn1_object_new (); + + e_asn1_object_set_display_value (printableItem, text); + e_asn1_object_set_display_name (printableItem, _("Subject's Public Key")); + e_asn1_object_append_child (spkiSequence, printableItem); + g_object_unref (printableItem); + g_free (text); + + e_asn1_object_append_child (parentSequence, spkiSequence); + g_object_unref (spkiSequence); + + return TRUE; +} + +static gboolean +process_ns_cert_type_extensions (SECItem *extData, + GString *text) +{ + SECItem decoded; + guchar nsCertType; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem (NULL, &decoded, + SEC_ASN1_GET (SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + nsCertType = decoded.data[0]; + + PORT_Free (decoded.data); /* XXX right free? */ + + if (nsCertType & NS_CERT_TYPE_SSL_CLIENT) { + g_string_append (text, _("SSL Client Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_SERVER) { + g_string_append (text, _("SSL Server Certificate")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL) { + g_string_append (text, _("Email")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_SSL_CA) { + g_string_append (text, _("SSL Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_EMAIL_CA) { + g_string_append (text, _("Email Certificate Authority")); + g_string_append (text, "\n"); + } + if (nsCertType & NS_CERT_TYPE_OBJECT_SIGNING_CA) { + g_string_append (text, _("Object Signer")); + g_string_append (text, "\n"); + } + return TRUE; +} + +static gboolean +process_key_usage_extensions (SECItem *extData, + GString *text) +{ + SECItem decoded; + guchar keyUsage; + + decoded.data = NULL; + decoded.len = 0; + if (SECSuccess != SEC_ASN1DecodeItem (NULL, &decoded, + SEC_ASN1_GET (SEC_BitStringTemplate), extData)) { + g_string_append (text, _("Error: Unable to process extension")); + return TRUE; + } + + keyUsage = decoded.data[0]; + PORT_Free (decoded.data); /* XXX right free? */ + + if (keyUsage & KU_DIGITAL_SIGNATURE) { + g_string_append (text, _("Signing")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_NON_REPUDIATION) { + g_string_append (text, _("Non-repudiation")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_ENCIPHERMENT) { + g_string_append (text, _("Key Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_DATA_ENCIPHERMENT) { + g_string_append (text, _("Data Encipherment")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_AGREEMENT) { + g_string_append (text, _("Key Agreement")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_KEY_CERT_SIGN) { + g_string_append (text, _("Certificate Signer")); + g_string_append (text, "\n"); + } + if (keyUsage & KU_CRL_SIGN) { + g_string_append (text, _("CRL Signer")); + g_string_append (text, "\n"); + } + + return TRUE; +} + +static gboolean +process_extension_data (SECOidTag oidTag, + SECItem *extData, + GString *str) +{ + gboolean rv; + switch (oidTag) { + case SEC_OID_NS_CERT_EXT_CERT_TYPE: + rv = process_ns_cert_type_extensions (extData, str); + break; + case SEC_OID_X509_KEY_USAGE: + rv = process_key_usage_extensions (extData, str); + break; + default: { + gchar *text; + rv = process_raw_bytes (extData, &text); + g_string_append (str, text); + g_free (text); + break; + } + } + return rv; +} + +static gboolean +process_single_extension (CERTCertExtension *extension, + EASN1Object **retExtension) +{ + GString *str = g_string_new (""); + gchar *text; + EASN1Object *extensionItem; + SECOidTag oidTag = SECOID_FindOIDTag (&extension->id); + + get_oid_text (&extension->id, &text); + + extensionItem = e_asn1_object_new (); + + e_asn1_object_set_display_name (extensionItem, text); + g_free (text); + + if (extension->critical.data != NULL) { + if (extension->critical.data[0]) { + g_string_append (str, _("Critical")); + } else { + g_string_append (str, _("Not Critical")); + } + } else { + g_string_append (str, _("Not Critical")); + } + g_string_append (str, "\n"); + if (!process_extension_data (oidTag, &extension->value, str)) { + g_string_free (str, TRUE); + return FALSE; + } + + e_asn1_object_set_display_value (extensionItem, str->str); + g_string_free (str, TRUE); + *retExtension = extensionItem; + return TRUE; +} + +static gboolean +process_extensions (CERTCertExtension **extensions, + EASN1Object *parentSequence) +{ + EASN1Object *extensionSequence = e_asn1_object_new (); + PRInt32 i; + + e_asn1_object_set_display_name (extensionSequence, _("Extensions")); + + for (i = 0; extensions[i] != NULL; i++) { + EASN1Object *newExtension; + + if (!process_single_extension (extensions[i], + &newExtension)) + return FALSE; + + e_asn1_object_append_child (extensionSequence, newExtension); + } + e_asn1_object_append_child (parentSequence, extensionSequence); + return TRUE; +} + +static gboolean +process_name (CERTName *name, + gchar **value) +{ + CERTRDN ** rdns; + CERTRDN ** rdn; + CERTAVA ** avas; + CERTAVA * ava; + SECItem *decodeItem = NULL; + GString *final_string = g_string_new (""); + + gchar *type; + GString *avavalue; + gchar *temp; + CERTRDN **lastRdn; + + rdns = name->rdns; + + /* find last RDN */ + lastRdn = rdns; + while (*lastRdn) lastRdn++; + + /* The above whille loop will put us at the last member + * of the array which is a NULL pointer. So let's back + * up one spot so that we have the last non-NULL entry in + * the array in preparation for traversing the + * RDN's (Relative Distinguished Name) in reverse order. + */ + lastRdn--; + + /* + * Loop over name contents in _reverse_ RDN order appending to string + * When building the Ascii string, NSS loops over these entries in + * reverse order, so I will as well. The difference is that NSS + * will always place them in a one line string separated by commas, + * where I want each entry on a single line. I can't just use a comma + * as my delimitter because it is a valid character to have in the + * value portion of the AVA and could cause trouble when parsing. + */ + for (rdn = lastRdn; rdn >= rdns; rdn--) { + avas = (*rdn)->avas; + while ((ava = *avas++) != 0) { + if (!get_oid_text (&ava->type, &type)) + return FALSE; + + /* This function returns a string in UTF8 format. */ + decodeItem = CERT_DecodeAVAValue (&ava->value); + if (!decodeItem) { + g_free (type); + return FALSE; + } + + avavalue = g_string_new_len ( + (gchar *) decodeItem->data, decodeItem->len); + + SECITEM_FreeItem (decodeItem, PR_TRUE); + + /* Translators: This string is used in Certificate + * details for fields like Issuer or Subject, which + * shows the field name on the left and its respective + * value on the right, both as stored in the + * certificate itself. You probably do not need to + * change this string, unless changing the order of + * name and value. As a result example: + * "OU = VeriSign Trust Network" */ + temp = g_strdup_printf (_("%s = %s"), type, avavalue->str); + + g_string_append (final_string, temp); + g_string_append (final_string, "\n"); + g_string_free (avavalue, TRUE); + g_free (temp); + g_free (type); + } + } + *value = g_string_free (final_string, FALSE); + return TRUE; +} + +static gboolean +create_tbs_certificate_asn1_struct (CERTCertificate *cert, + EASN1Object **seq) +{ + /* + ** TBSCertificate ::= SEQUENCE { + ** version [0] EXPLICIT Version DEFAULT v1, + ** serialNumber CertificateSerialNumber, + ** signature AlgorithmIdentifier, + ** issuer Name, + ** validity Validity, + ** subject Name, + ** subjectPublicKeyInfo SubjectPublicKeyInfo, + ** issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + ** -- If present, version shall be v2 or v3 + ** extensions [3] EXPLICIT Extensions OPTIONAL + ** -- If present, version shall be v3 + ** } + ** + ** This is the ASN1 structure we should be dealing with at this point. + ** The code in this method will assert this is the structure we're dealing + ** and then add more user friendly text for that field. + */ + EASN1Object *sequence = e_asn1_object_new (); + gchar *text; + EASN1Object *subitem; + SECItem data; + + e_asn1_object_set_display_name (sequence, _("Certificate")); + + if (!process_version (&cert->version, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_serial_number_der (&cert->serialNumber, &subitem)) + return FALSE; + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_sec_algorithm_id (&cert->signature, &subitem)) + return FALSE; + e_asn1_object_set_display_name (subitem, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + process_name (&cert->issuer, &text); + subitem = e_asn1_object_new (); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_set_display_name (subitem, _("Issuer")); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + +#ifdef notyet + nsCOMPtr < nsIASN1Sequence> validitySequence = new nsNSSASN1Sequence (); + nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpValidity").get (), + text); + validitySequence->SetDisplayName (text); + asn1Objects->AppendElement (validitySequence, PR_FALSE); + nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpNotBefore").get (), + text); + nsCOMPtr < nsIX509CertValidity> validityData; + GetValidity (getter_AddRefs (validityData)); + PRTime notBefore, notAfter; + + validityData->GetNotBefore (¬Before); + validityData->GetNotAfter (¬After); + validityData = 0; + rv = ProcessTime (notBefore, text.get (), validitySequence); + if (NS_FAILED (rv)) + return rv; + + nssComponent->GetPIPNSSBundleString (NS_LITERAL_STRING ("CertDumpNotAfter").get (), + text); + rv = ProcessTime (notAfter, text.get (), validitySequence); + if (NS_FAILED (rv)) + return rv; +#endif + + subitem = e_asn1_object_new (); + e_asn1_object_set_display_name (subitem, _("Subject")); + + process_name (&cert->subject, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + e_asn1_object_append_child (sequence, subitem); + g_object_unref (subitem); + + if (!process_subject_public_key_info (&cert->subjectPublicKeyInfo, sequence)) + return FALSE; + + /* Is there an issuerUniqueID? */ + if (cert->issuerID.data) { + /* The issuerID is encoded as a bit string. + * The function ProcessRawBytes expects the + * length to be in bytes, so let's convert the + * length in a temporary SECItem + */ + data.data = cert->issuerID.data; + data.len = cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Issuer Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + + if (cert->subjectID.data) { + /* The subjectID is encoded as a bit string. + * The function ProcessRawBytes expects the + * length to be in bytes, so let's convert the + * length in a temporary SECItem + */ + data.data = cert->issuerID.data; + data.len = cert->issuerID.len / 8; + + subitem = e_asn1_object_new (); + + e_asn1_object_set_display_name (subitem, _("Subject Unique ID")); + process_raw_bytes (&data, &text); + e_asn1_object_set_display_value (subitem, text); + g_free (text); + + e_asn1_object_append_child (sequence, subitem); + } + if (cert->extensions) { + if (!process_extensions (cert->extensions, sequence)) + return FALSE; + } + + *seq = sequence; + + return TRUE; +} + +static gboolean +fill_asn1_from_cert (EASN1Object *asn1, + CERTCertificate *cert) +{ + EASN1Object *sequence; + SECItem temp; + gchar *text; + + g_return_val_if_fail (asn1 != NULL, FALSE); + g_return_val_if_fail (cert != NULL, FALSE); + + if (cert->nickname) { + e_asn1_object_set_display_name (asn1, cert->nickname); + } else { + gchar *str; + + str = CERT_GetCommonName (&cert->subject); + if (str) { + e_asn1_object_set_display_name (asn1, str); + PORT_Free (str); + } else { + e_asn1_object_set_display_name (asn1, cert->subjectName); + } + } + + /* This sequence will be contain the tbsCertificate, signatureAlgorithm, + * and signatureValue. */ + + if (!create_tbs_certificate_asn1_struct (cert, &sequence)) + return FALSE; + e_asn1_object_append_child (asn1, sequence); + g_object_unref (sequence); + + if (!process_sec_algorithm_id (&cert->signatureWrap.signatureAlgorithm, &sequence)) + return FALSE; + e_asn1_object_set_display_name ( + sequence, _("Certificate Signature Algorithm")); + e_asn1_object_append_child (asn1, sequence); + g_object_unref (sequence); + + sequence = e_asn1_object_new (); + e_asn1_object_set_display_name ( + sequence, _("Certificate Signature Value")); + + /* The signatureWrap is encoded as a bit string. + * The function ProcessRawBytes expects the + * length to be in bytes, so let's convert the + * length in a temporary SECItem */ + temp.data = cert->signatureWrap.signature.data; + temp.len = cert->signatureWrap.signature.len / 8; + process_raw_bytes (&temp, &text); + e_asn1_object_set_display_value (sequence, text); + e_asn1_object_append_child (asn1, sequence); + g_free (text); + + return TRUE; +} + +static void +e_asn1_object_finalize (GObject *object) +{ + EASN1ObjectPrivate *priv; + + priv = E_ASN1_OBJECT_GET_PRIVATE (object); + + g_free (priv->display_name); + g_free (priv->value); + + g_list_free_full (priv->children, (GDestroyNotify) g_object_unref); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_asn1_object_parent_class)->finalize (object); +} + +static void +e_asn1_object_class_init (EASN1ObjectClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EASN1ObjectPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_asn1_object_finalize; +} + +static void +e_asn1_object_init (EASN1Object *asn1) +{ + asn1->priv = E_ASN1_OBJECT_GET_PRIVATE (asn1); + + asn1->priv->valid_container = TRUE; +} + +EASN1Object * +e_asn1_object_new (void) +{ + return E_ASN1_OBJECT (g_object_new (E_TYPE_ASN1_OBJECT, NULL)); +} + +EASN1Object * +e_asn1_object_new_from_cert (CERTCertificate *cert) +{ + EASN1Object *asn1; + + g_return_val_if_fail (cert != NULL, NULL); + + asn1 = e_asn1_object_new (); + if (!fill_asn1_from_cert (asn1, cert)) { + g_object_unref (asn1); + return NULL; + } + + return asn1; +} + +void +e_asn1_object_set_valid_container (EASN1Object *obj, + gboolean flag) +{ + obj->priv->valid_container = flag; +} + +gboolean +e_asn1_object_is_valid_container (EASN1Object *obj) +{ + return obj->priv->valid_container; +} + +PRUint32 +e_asn1_object_get_asn1_type (EASN1Object *obj) +{ + return obj->priv->type; +} + +PRUint32 +e_asn1_object_get_asn1_tag (EASN1Object *obj) +{ + return obj->priv->tag; +} + +GList * +e_asn1_object_get_children (EASN1Object *obj) +{ + GList *children = g_list_copy (obj->priv->children); + + g_list_foreach (children, (GFunc) g_object_ref, NULL); + + return children; +} + +void +e_asn1_object_append_child (EASN1Object *parent, + EASN1Object *child) +{ + parent->priv->children = g_list_append ( + parent->priv->children, g_object_ref (child)); +} + +void +e_asn1_object_set_display_name (EASN1Object *obj, + const gchar *name) +{ + g_free (obj->priv->display_name); + obj->priv->display_name = g_strdup (name); +} + +const gchar * +e_asn1_object_get_display_name (EASN1Object *obj) +{ + return obj->priv->display_name; +} + +void +e_asn1_object_set_display_value (EASN1Object *obj, + const gchar *value) +{ + g_free (obj->priv->value); + obj->priv->value = g_strdup (value); +} + +const gchar * +e_asn1_object_get_display_value (EASN1Object *obj) +{ + return obj->priv->value; +} + +void +e_asn1_object_get_data (EASN1Object *obj, + gchar **data, + guint32 *len) +{ + *data = obj->priv->data; + *len = obj->priv->data_len; +} diff --git a/smime/lib/e-asn1-object.h b/smime/lib/e-asn1-object.h new file mode 100644 index 0000000000..39b79a7b1e --- /dev/null +++ b/smime/lib/e-asn1-object.h @@ -0,0 +1,109 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifndef E_ASN1_OBJECT_H +#define E_ASN1_OBJECT_H + +#include <glib-object.h> + +#include <cert.h> + +#define E_TYPE_ASN1_OBJECT (e_asn1_object_get_type ()) +#define E_ASN1_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_ASN1_OBJECT, EASN1Object)) +#define E_ASN1_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_ASN1_OBJECT, EASN1ObjectClass)) +#define E_IS_ASN1_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_ASN1_OBJECT)) +#define E_IS_ASN1_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_ASN1_OBJECT)) +#define E_ASN1_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_ASN1_OBJECT, EASN1ObjectClass)) + +typedef struct _EASN1Object EASN1Object; +typedef struct _EASN1ObjectClass EASN1ObjectClass; +typedef struct _EASN1ObjectPrivate EASN1ObjectPrivate; + +enum { + /* + * Identifiers for the possible types of object. + */ + E_ASN1_OBJECT_TYPE_END_CONTENTS = 0, + E_ASN1_OBJECT_TYPE_BOOLEAN = 1, + E_ASN1_OBJECT_TYPE_INTEGER = 2, + E_ASN1_OBJECT_TYPE_BIT_STRING = 3, + E_ASN1_OBJECT_TYPE_OCTET_STRING = 4, + E_ASN1_OBJECT_TYPE_NULL = 5, + E_ASN1_OBJECT_TYPE_OBJECT_ID = 6, + E_ASN1_OBJECT_TYPE_ENUMERATED = 10, + E_ASN1_OBJECT_TYPE_UTF8_STRING = 12, + E_ASN1_OBJECT_TYPE_SEQUENCE = 16, + E_ASN1_OBJECT_TYPE_SET = 17, + E_ASN1_OBJECT_TYPE_PRINTABLE_STRING = 19, + E_ASN1_OBJECT_TYPE_T61_STRING = 20, + E_ASN1_OBJECT_TYPE_IA5_STRING = 22, + E_ASN1_OBJECT_TYPE_UTC_TIME = 23, + E_ASN1_OBJECT_TYPE_GEN_TIME = 24, + E_ASN1_OBJECT_TYPE_VISIBLE_STRING = 26, + E_ASN1_OBJECT_TYPE_UNIVERSAL_STRING = 28, + E_ASN1_OBJECT_TYPE_BMP_STRING = 30, + E_ASN1_OBJECT_TYPE_HIGH_TAG_NUMBER = 31, + E_ASN1_OBJECT_TYPE_CONTEXT_SPECIFIC = 32, + E_ASN1_OBJECT_TYPE_APPLICATION = 33, + E_ASN1_OBJECT_TYPE_PRIVATE = 34 +}; + +struct _EASN1Object { + GObject parent; + + EASN1ObjectPrivate *priv; +}; + +struct _EASN1ObjectClass { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_ecert_reserved0) (void); + void (*_ecert_reserved1) (void); + void (*_ecert_reserved2) (void); + void (*_ecert_reserved3) (void); + void (*_ecert_reserved4) (void); +}; + +GType e_asn1_object_get_type (void); +EASN1Object * e_asn1_object_new (void); +EASN1Object * e_asn1_object_new_from_cert (CERTCertificate *cert); + +void e_asn1_object_set_valid_container (EASN1Object *obj, + gboolean flag); +gboolean e_asn1_object_is_valid_container (EASN1Object *obj); +PRUint32 e_asn1_object_get_asn1_type (EASN1Object *obj); +PRUint32 e_asn1_object_get_asn1_tag (EASN1Object *obj); +GList * e_asn1_object_get_children (EASN1Object *obj); +void e_asn1_object_append_child (EASN1Object *parent, + EASN1Object *child); +void e_asn1_object_set_display_name (EASN1Object *obj, + const gchar *name); +const gchar * e_asn1_object_get_display_name (EASN1Object *obj); +void e_asn1_object_set_display_value (EASN1Object *obj, + const gchar *value); +const gchar * e_asn1_object_get_display_value (EASN1Object *obj); + +void e_asn1_object_get_data (EASN1Object *obj, + gchar **data, + guint32 *len); + +#endif /* E_ASN1_OBJECT_H */ diff --git a/smime/lib/e-cert-db.c b/smime/lib/e-cert-db.c new file mode 100644 index 0000000000..bb4a68e61d --- /dev/null +++ b/smime/lib/e-cert-db.c @@ -0,0 +1,1521 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* e-cert-db.c + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <camel/camel.h> + +/* private NSS defines used by PSM */ +/* (must be declated before cert.h) */ +#define CERT_NewTempCertificate __CERT_NewTempCertificate +#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm + +#include "e-cert-db.h" +#include "e-cert-trust.h" +#include "e-pkcs12.h" + +#include "gmodule.h" + +#include "nss.h" +#include "ssl.h" +#include "p12plcy.h" +#include "pk11func.h" +#include "nssckbi.h" +#include <secerr.h> +#include "secmod.h" +#include "certdb.h" +#include "plstr.h" +#include "prprf.h" +#include "prmem.h" +#include "e-util/e-util.h" +#include "e-util/e-util-private.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +enum { + PK11_PASSWD, + PK11_CHANGE_PASSWD, + CONFIRM_CA_CERT_IMPORT, + LAST_SIGNAL +}; + +static guint e_cert_db_signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (ECertDB, e_cert_db, G_TYPE_OBJECT) + +GQuark +e_certdb_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_static_string ("e-certdb-error-quark"); + + return q; +} + +static const gchar * +nss_error_to_string (glong errorcode) +{ +#define cs(a,b) case a: return b; + + switch (errorcode) { + cs (SEC_ERROR_IO, "An I/O error occurred during security authorization.") + cs (SEC_ERROR_LIBRARY_FAILURE, "security library failure.") + cs (SEC_ERROR_BAD_DATA, "security library: received bad data.") + cs (SEC_ERROR_OUTPUT_LEN, "security library: output length error.") + cs (SEC_ERROR_INPUT_LEN, "security library has experienced an input length error.") + cs (SEC_ERROR_INVALID_ARGS, "security library: invalid arguments.") + cs (SEC_ERROR_INVALID_ALGORITHM, "security library: invalid algorithm.") + cs (SEC_ERROR_INVALID_AVA, "security library: invalid AVA.") + cs (SEC_ERROR_INVALID_TIME, "Improperly formatted time string.") + cs (SEC_ERROR_BAD_DER, "security library: improperly formatted DER-encoded message.") + cs (SEC_ERROR_BAD_SIGNATURE, "Peer's certificate has an invalid signature.") + cs (SEC_ERROR_EXPIRED_CERTIFICATE, "Peer's Certificate has expired.") + cs (SEC_ERROR_REVOKED_CERTIFICATE, "Peer's Certificate has been revoked.") + cs (SEC_ERROR_UNKNOWN_ISSUER, "Peer's Certificate issuer is not recognized.") + cs (SEC_ERROR_BAD_KEY, "Peer's public key is invalid.") + cs (SEC_ERROR_BAD_PASSWORD, "The security password entered is incorrect.") + cs (SEC_ERROR_RETRY_PASSWORD, "New password entered incorrectly. Please try again.") + cs (SEC_ERROR_NO_NODELOCK, "security library: no nodelock.") + cs (SEC_ERROR_BAD_DATABASE, "security library: bad database.") + cs (SEC_ERROR_NO_MEMORY, "security library: memory allocation failure.") + cs (SEC_ERROR_UNTRUSTED_ISSUER, "Peer's certificate issuer has been marked as not trusted by the user.") + cs (SEC_ERROR_UNTRUSTED_CERT, "Peer's certificate has been marked as not trusted by the user.") + cs (SEC_ERROR_DUPLICATE_CERT, "Certificate already exists in your database.") + cs (SEC_ERROR_DUPLICATE_CERT_NAME, "Downloaded certificate's name duplicates one already in your database.") + cs (SEC_ERROR_ADDING_CERT, "Error adding certificate to database.") + cs (SEC_ERROR_FILING_KEY, "Error refiling the key for this certificate.") + cs (SEC_ERROR_NO_KEY, "The private key for this certificate cannot be found in key database") + cs (SEC_ERROR_CERT_VALID, "This certificate is valid.") + cs (SEC_ERROR_CERT_NOT_VALID, "This certificate is not valid.") + cs (SEC_ERROR_CERT_NO_RESPONSE, "Cert Library: No Response") + cs (SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, "The certificate issuer's certificate has expired. Check your system date and time.") + cs (SEC_ERROR_CRL_EXPIRED, "The CRL for the certificate's issuer has expired. Update it or check your system date and time.") + cs (SEC_ERROR_CRL_BAD_SIGNATURE, "The CRL for the certificate's issuer has an invalid signature.") + cs (SEC_ERROR_CRL_INVALID, "New CRL has an invalid format.") + cs (SEC_ERROR_EXTENSION_VALUE_INVALID, "Certificate extension value is invalid.") + cs (SEC_ERROR_EXTENSION_NOT_FOUND, "Certificate extension not found.") + cs (SEC_ERROR_CA_CERT_INVALID, "Issuer certificate is invalid.") + cs (SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID, "Certificate path length constraint is invalid.") + cs (SEC_ERROR_CERT_USAGES_INVALID, "Certificate usages field is invalid.") + cs (SEC_INTERNAL_ONLY, "**Internal ONLY module**") + cs (SEC_ERROR_INVALID_KEY, "The key does not support the requested operation.") + cs (SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, "Certificate contains unknown critical extension.") + cs (SEC_ERROR_OLD_CRL, "New CRL is not later than the current one.") + cs (SEC_ERROR_NO_EMAIL_CERT, "Not encrypted or signed: you do not yet have an email certificate.") + cs (SEC_ERROR_NO_RECIPIENT_CERTS_QUERY, "Not encrypted: you do not have certificates for each of the recipients.") + cs (SEC_ERROR_NOT_A_RECIPIENT, "Cannot decrypt: you are not a recipient, or matching certificate and private key not found.") + cs (SEC_ERROR_PKCS7_KEYALG_MISMATCH, "Cannot decrypt: key encryption algorithm does not match your certificate.") + cs (SEC_ERROR_PKCS7_BAD_SIGNATURE, "Signature verification failed: no signer found, too many signers found, or improper or corrupted data.") + cs (SEC_ERROR_UNSUPPORTED_KEYALG, "Unsupported or unknown key algorithm.") + cs (SEC_ERROR_DECRYPTION_DISALLOWED, "Cannot decrypt: encrypted using a disallowed algorithm or key size.") + cs (XP_SEC_FORTEZZA_BAD_CARD, "Fortezza card has not been properly initialized. Please remove it and return it to your issuer.") + cs (XP_SEC_FORTEZZA_NO_CARD, "No Fortezza cards Found") + cs (XP_SEC_FORTEZZA_NONE_SELECTED, "No Fortezza card selected") + cs (XP_SEC_FORTEZZA_MORE_INFO, "Please select a personality to get more info on") + cs (XP_SEC_FORTEZZA_PERSON_NOT_FOUND, "Personality not found") + cs (XP_SEC_FORTEZZA_NO_MORE_INFO, "No more information on that Personality") + cs (XP_SEC_FORTEZZA_BAD_PIN, "Invalid Pin") + cs (XP_SEC_FORTEZZA_PERSON_ERROR, "Couldn't initialize Fortezza personalities.") + cs (SEC_ERROR_NO_KRL, "No KRL for this site's certificate has been found.") + cs (SEC_ERROR_KRL_EXPIRED, "The KRL for this site's certificate has expired.") + cs (SEC_ERROR_KRL_BAD_SIGNATURE, "The KRL for this site's certificate has an invalid signature.") + cs (SEC_ERROR_REVOKED_KEY, "The key for this site's certificate has been revoked.") + cs (SEC_ERROR_KRL_INVALID, "New KRL has an invalid format.") + cs (SEC_ERROR_NEED_RANDOM, "security library: need random data.") + cs (SEC_ERROR_NO_MODULE, "security library: no security module can perform the requested operation.") + cs (SEC_ERROR_NO_TOKEN, "The security card or token does not exist, needs to be initialized, or has been removed.") + cs (SEC_ERROR_READ_ONLY, "security library: read-only database.") + cs (SEC_ERROR_NO_SLOT_SELECTED, "No slot or token was selected.") + cs (SEC_ERROR_CERT_NICKNAME_COLLISION, "A certificate with the same nickname already exists.") + cs (SEC_ERROR_KEY_NICKNAME_COLLISION, "A key with the same nickname already exists.") + cs (SEC_ERROR_SAFE_NOT_CREATED, "error while creating safe object") + cs (SEC_ERROR_BAGGAGE_NOT_CREATED, "error while creating baggage object") + cs (XP_JAVA_REMOVE_PRINCIPAL_ERROR, "Couldn't remove the principal") + cs (XP_JAVA_DELETE_PRIVILEGE_ERROR, "Couldn't delete the privilege") + cs (XP_JAVA_CERT_NOT_EXISTS_ERROR, "This principal doesn't have a certificate") + cs (SEC_ERROR_BAD_EXPORT_ALGORITHM, "Required algorithm is not allowed.") + cs (SEC_ERROR_EXPORTING_CERTIFICATES, "Error attempting to export certificates.") + cs (SEC_ERROR_IMPORTING_CERTIFICATES, "Error attempting to import certificates.") + cs (SEC_ERROR_PKCS12_DECODING_PFX, "Unable to import. Decoding error. File not valid.") + cs (SEC_ERROR_PKCS12_INVALID_MAC, "Unable to import. Invalid MAC. Incorrect password or corrupt file.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM, "Unable to import. MAC algorithm not supported.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE, "Unable to import. Only password integrity and privacy modes supported.") + cs (SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE, "Unable to import. File structure is corrupt.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM, "Unable to import. Encryption algorithm not supported.") + cs (SEC_ERROR_PKCS12_UNSUPPORTED_VERSION, "Unable to import. File version not supported.") + cs (SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT, "Unable to import. Incorrect privacy password.") + cs (SEC_ERROR_PKCS12_CERT_COLLISION, "Unable to import. Same nickname already exists in database.") + cs (SEC_ERROR_USER_CANCELLED, "The user pressed cancel.") + cs (SEC_ERROR_PKCS12_DUPLICATE_DATA, "Not imported, already in database.") + cs (SEC_ERROR_MESSAGE_SEND_ABORTED, "Message not sent.") + cs (SEC_ERROR_INADEQUATE_KEY_USAGE, "Certificate key usage inadequate for attempted operation.") + cs (SEC_ERROR_INADEQUATE_CERT_TYPE, "Certificate type not approved for application.") + cs (SEC_ERROR_CERT_ADDR_MISMATCH, "Address in signing certificate does not match address in message headers.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY, "Unable to import. Error attempting to import private key.") + cs (SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN, "Unable to import. Error attempting to import certificate chain.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME, "Unable to export. Unable to locate certificate or key by nickname.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY, "Unable to export. Private Key could not be located and exported.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_WRITE, "Unable to export. Unable to write the export file.") + cs (SEC_ERROR_PKCS12_UNABLE_TO_READ, "Unable to import. Unable to read the import file.") + cs (SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED, "Unable to export. Key database corrupt or deleted.") + cs (SEC_ERROR_KEYGEN_FAIL, "Unable to generate public/private key pair.") + cs (SEC_ERROR_INVALID_PASSWORD, "Password entered is invalid. Please pick a different one.") + cs (SEC_ERROR_RETRY_OLD_PASSWORD, "Old password entered incorrectly. Please try again.") + cs (SEC_ERROR_BAD_NICKNAME, "Certificate nickname already in use.") + cs (SEC_ERROR_NOT_FORTEZZA_ISSUER, "Peer FORTEZZA chain has a non-FORTEZZA Certificate.") + cs (SEC_ERROR_CANNOT_MOVE_SENSITIVE_KEY, "A sensitive key cannot be moved to the slot where it is needed.") + cs (SEC_ERROR_JS_INVALID_MODULE_NAME, "Invalid module name.") + cs (SEC_ERROR_JS_INVALID_DLL, "Invalid module path/filename") + cs (SEC_ERROR_JS_ADD_MOD_FAILURE, "Unable to add module") + cs (SEC_ERROR_JS_DEL_MOD_FAILURE, "Unable to delete module") + cs (SEC_ERROR_OLD_KRL, "New KRL is not later than the current one.") + cs (SEC_ERROR_CKL_CONFLICT, "New CKL has different issuer than current CKL. Delete current CKL.") + cs (SEC_ERROR_CERT_NOT_IN_NAME_SPACE, "The Certifying Authority for this certificate is not permitted to issue a certificate with this name.") + cs (SEC_ERROR_KRL_NOT_YET_VALID, "The key revocation list for this certificate is not yet valid.") + cs (SEC_ERROR_CRL_NOT_YET_VALID, "The certificate revocation list for this certificate is not yet valid.") + cs (SEC_ERROR_UNKNOWN_CERT, "The requested certificate could not be found.") + cs (SEC_ERROR_UNKNOWN_SIGNER, "The signer's certificate could not be found.") + cs (SEC_ERROR_CERT_BAD_ACCESS_LOCATION, "The location for the certificate status server has invalid format.") + cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE, "The OCSP response cannot be fully decoded; it is of an unknown type.") + cs (SEC_ERROR_OCSP_BAD_HTTP_RESPONSE, "The OCSP server returned unexpected/invalid HTTP data.") + cs (SEC_ERROR_OCSP_MALFORMED_REQUEST, "The OCSP server found the request to be corrupted or improperly formed.") + cs (SEC_ERROR_OCSP_SERVER_ERROR, "The OCSP server experienced an internal error.") + cs (SEC_ERROR_OCSP_TRY_SERVER_LATER, "The OCSP server suggests trying again later.") + cs (SEC_ERROR_OCSP_REQUEST_NEEDS_SIG, "The OCSP server requires a signature on this request.") + cs (SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST, "The OCSP server has refused this request as unauthorized.") + cs (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, "The OCSP server returned an unrecognizable status.") + cs (SEC_ERROR_OCSP_UNKNOWN_CERT, "The OCSP server has no status for the certificate.") + cs (SEC_ERROR_OCSP_NOT_ENABLED, "You must enable OCSP before performing this operation.") + cs (SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER, "You must set the OCSP default responder before performing this operation.") + cs (SEC_ERROR_OCSP_MALFORMED_RESPONSE, "The response from the OCSP server was corrupted or improperly formed.") + cs (SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE, "The signer of the OCSP response is not authorized to give status for this certificate.") + cs (SEC_ERROR_OCSP_FUTURE_RESPONSE, "The OCSP response is not yet valid (contains a date in the future).") + cs (SEC_ERROR_OCSP_OLD_RESPONSE, "The OCSP response contains out-of-date information.") + cs (SEC_ERROR_DIGEST_NOT_FOUND, "The CMS or PKCS #7 Digest was not found in signed message.") + cs (SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE, "The CMS or PKCS #7 Message type is unsupported.") + cs (SEC_ERROR_MODULE_STUCK, "PKCS #11 module could not be removed because it is still in use.") + cs (SEC_ERROR_BAD_TEMPLATE, "Could not decode ASN.1 data. Specified template was invalid.") + cs (SEC_ERROR_CRL_NOT_FOUND, "No matching CRL was found.") + cs (SEC_ERROR_REUSED_ISSUER_AND_SERIAL, "You are attempting to import a cert with the same issuer/serial as an existing cert, but that is not the same cert.") + cs (SEC_ERROR_BUSY, "NSS could not shutdown. Objects are still in use.") + cs (SEC_ERROR_EXTRA_INPUT, "DER-encoded message contained extra unused data.") + cs (SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE, "Unsupported elliptic curve.") + cs (SEC_ERROR_UNSUPPORTED_EC_POINT_FORM, "Unsupported elliptic curve point form.") + cs (SEC_ERROR_UNRECOGNIZED_OID, "Unrecognized Object Identifier.") + cs (SEC_ERROR_OCSP_INVALID_SIGNING_CERT, "Invalid OCSP signing certificate in OCSP response.") + cs (SEC_ERROR_REVOKED_CERTIFICATE_CRL, "Certificate is revoked in issuer's certificate revocation list.") + cs (SEC_ERROR_REVOKED_CERTIFICATE_OCSP, "Issuer's OCSP responder reports certificate is revoked.") + cs (SEC_ERROR_CRL_INVALID_VERSION, "Issuer's Certificate Revocation List has an unknown version number.") + cs (SEC_ERROR_CRL_V1_CRITICAL_EXTENSION, "Issuer's V1 Certificate Revocation List has a critical extension.") + cs (SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION, "Issuer's V2 Certificate Revocation List has an unknown critical extension.") + cs (SEC_ERROR_UNKNOWN_OBJECT_TYPE, "Unknown object type specified.") + cs (SEC_ERROR_INCOMPATIBLE_PKCS11, "PKCS #11 driver violates the spec in an incompatible way.") + cs (SEC_ERROR_NO_EVENT, "No new slot event is available at this time.") + cs (SEC_ERROR_CRL_ALREADY_EXISTS, "CRL already exists.") + cs (SEC_ERROR_NOT_INITIALIZED, "NSS is not initialized.") + cs (SEC_ERROR_TOKEN_NOT_LOGGED_IN, "The operation failed because the PKCS#11 token is not logged in.") + cs (SEC_ERROR_OCSP_RESPONDER_CERT_INVALID, "Configured OCSP responder's certificate is invalid.") + cs (SEC_ERROR_OCSP_BAD_SIGNATURE, "OCSP response has an invalid signature.") + + #if defined (NSS_VMAJOR) && defined (NSS_VMINOR) && defined (NSS_VPATCH) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && NSS_VMINOR > 12) || (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 2)) + cs (SEC_ERROR_OUT_OF_SEARCH_LIMITS, "Cert validation search is out of search limits") + cs (SEC_ERROR_INVALID_POLICY_MAPPING, "Policy mapping contains anypolicy") + cs (SEC_ERROR_POLICY_VALIDATION_FAILED, "Cert chain fails policy validation") + cs (SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE, "Unknown location type in cert AIA extension") + cs (SEC_ERROR_BAD_HTTP_RESPONSE, "Server returned bad HTTP response") + cs (SEC_ERROR_BAD_LDAP_RESPONSE, "Server returned bad LDAP response") + cs (SEC_ERROR_FAILED_TO_ENCODE_DATA, "Failed to encode data with ASN1 encoder") + cs (SEC_ERROR_BAD_INFO_ACCESS_LOCATION, "Bad information access location in cert extension") + cs (SEC_ERROR_LIBPKIX_INTERNAL, "Libpkix internal error occured during cert validation.") + cs (SEC_ERROR_PKCS11_GENERAL_ERROR, "A PKCS #11 module returned CKR_GENERAL_ERROR, indicating that an unrecoverable error has occurred.") + cs (SEC_ERROR_PKCS11_FUNCTION_FAILED, "A PKCS #11 module returned CKR_FUNCTION_FAILED, indicating that the requested function could not be performed. Trying the same operation again might succeed.") + cs (SEC_ERROR_PKCS11_DEVICE_ERROR, "A PKCS #11 module returned CKR_DEVICE_ERROR, indicating that a problem has occurred with the token or slot.") + #endif + } + + #undef cs + + return NULL; +} + +static void +set_nss_error (GError **error) +{ + glong err_code; + const gchar *err_str; + + if (!error) + return; + + g_return_if_fail (*error == NULL); + + err_code = PORT_GetError (); + + if (!err_code) + return; + + err_str = nss_error_to_string (err_code); + if (!err_str) + return; + + *error = g_error_new_literal (E_CERTDB_ERROR, err_code, err_str); +} + +static SECStatus PR_CALLBACK +collect_certs (gpointer arg, + SECItem **certs, + gint numcerts) +{ + CERTDERCerts *collectArgs; + SECItem *cert; + SECStatus rv; + + collectArgs = (CERTDERCerts *) arg; + + collectArgs->numcerts = numcerts; + collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc ( + collectArgs->arena, sizeof (SECItem) * numcerts); + if (collectArgs->rawCerts == NULL) + return (SECFailure); + + cert = collectArgs->rawCerts; + + while (numcerts--) { + rv = SECITEM_CopyItem (collectArgs->arena, cert, *certs); + if (rv == SECFailure) + return (SECFailure); + cert++; + certs++; + } + + return (SECSuccess); +} + +static CERTDERCerts * +e_cert_db_get_certs_from_package (PRArenaPool *arena, + gchar *data, + guint32 length) +{ + /*nsNSSShutDownPreventionLock locker;*/ + CERTDERCerts *collectArgs = + (CERTDERCerts *) PORT_ArenaZAlloc (arena, sizeof (CERTDERCerts)); + SECStatus sec_rv; + + if (!collectArgs) + return NULL; + + collectArgs->arena = arena; + sec_rv = CERT_DecodeCertPackage ( + data, + length, collect_certs, + (gpointer) collectArgs); + + if (sec_rv != SECSuccess) + return NULL; + + return collectArgs; +} + +#ifdef notyet +PRBool +ucs2_ascii_conversion_fn (PRBool toUnicode, + guchar *inBuf, + guint inBufLen, + guchar *outBuf, + guint maxOutBufLen, + guint *outBufLen, + PRBool swapBytes) +{ + printf ("in ucs2_ascii_conversion_fn\n"); +} +#endif + +static gchar * PR_CALLBACK +pk11_password (PK11SlotInfo *slot, + PRBool retry, + gpointer arg) +{ + gchar *pwd; + gchar *nsspwd; + + gboolean rv = FALSE; + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[PK11_PASSWD], 0, + slot, + retry, + &pwd, + &rv); + + if (pwd == NULL) + return NULL; + + nsspwd = PORT_Strdup (pwd); + memset (pwd, 0, strlen (pwd)); + g_free (pwd); + return nsspwd; +} + +static void +initialize_nss (void) +{ + /* Use camel_init() to initialise NSS consistently... */ + camel_init (e_get_user_data_dir (), TRUE); + + /* ... except for the bits we only seem to do here. FIXME */ + PK11_SetPasswordFunc (pk11_password); + + /* Enable ciphers for PKCS#12 */ + SEC_PKCS12EnableCipher (PKCS12_RC4_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC4_128, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_40, 1); + SEC_PKCS12EnableCipher (PKCS12_RC2_CBC_128, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_56, 1); + SEC_PKCS12EnableCipher (PKCS12_DES_EDE3_168, 1); + SEC_PKCS12SetPreferredCipher (PKCS12_DES_EDE3_168, 1); +#ifdef notyet + PORT_SetUCS2_ASCIIConversionFunction (ucs2_ascii_conversion_fn); +#endif +} + +static void +install_loadable_roots (void) +{ + SECMODModuleList *list = SECMOD_GetDefaultModuleList (); + SECMODListLock *lock = SECMOD_GetDefaultModuleListLock (); + SECMODModule *RootsModule = NULL; + gint i; + + SECMOD_GetReadLock (lock); + while (!RootsModule && list) { + SECMODModule *module = list->module; + + for (i = 0; i < module->slotCount; i++) { + PK11SlotInfo *slot = module->slots[i]; + if (PK11_IsPresent (slot)) { + if (PK11_HasRootCerts (slot)) { + RootsModule = module; + break; + } + } + } + + list = list->next; + } + SECMOD_ReleaseReadLock (lock); + + if (RootsModule) { + /* Check version, and unload module if it is too old */ + CK_INFO info; + + if (PK11_GetModInfo (RootsModule, &info) != SECSuccess) { + /* Do not use this module */ + RootsModule = NULL; + } else { + /* NSS_BUILTINS_LIBRARY_VERSION_MAJOR and NSS_BUILTINS_LIBRARY_VERSION_MINOR + * define the version we expect to have. + * Later version are fine. + * Older versions are not ok, and we will replace with our own version. + */ + if ((info.libraryVersion.major < NSS_BUILTINS_LIBRARY_VERSION_MAJOR) + || (info.libraryVersion.major == NSS_BUILTINS_LIBRARY_VERSION_MAJOR + && info.libraryVersion.minor < NSS_BUILTINS_LIBRARY_VERSION_MINOR)) { + PRInt32 modType; + + SECMOD_DeleteModule (RootsModule->commonName, &modType); + + RootsModule = NULL; + } + } + } + + if (!RootsModule) { +#ifndef G_OS_WIN32 + /* grovel in various places for mozilla's built-in + * cert module. + * + * XXX yes this is gross. *sigh * + */ + const gchar *paths_to_check[] = { +#ifdef MOZILLA_NSS_LIB_DIR + MOZILLA_NSS_LIB_DIR, +#endif + "/usr/lib", + "/usr/lib/mozilla", + "/opt/mozilla/lib", + "/opt/mozilla/lib/mozilla" + }; + + for (i = 0; i < G_N_ELEMENTS (paths_to_check); i++) { + gchar *dll_path = g_module_build_path (paths_to_check[i], "nssckbi"); + + if (g_file_test (dll_path, G_FILE_TEST_EXISTS)) { + PRInt32 modType; + + /* Delete the existing module */ + SECMOD_DeleteModule ("Mozilla Root Certs", &modType); + + SECMOD_AddNewModule ("Mozilla Root Certs",dll_path, 0, 0); + g_free (dll_path); + break; + } + + g_free (dll_path); + } +#else + /* FIXME: Might be useful to look up if there is a + * Mozilla installation on the machine and use the + * nssckbi.dll from there. + */ +#endif + } +} + +static void +e_cert_db_class_init (ECertDBClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + + initialize_nss (); + /* check to see if you have a rootcert module installed */ + install_loadable_roots (); + + e_cert_db_signals[PK11_PASSWD] = g_signal_new ( + "pk11_passwd", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, pk11_passwd), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_BOOLEAN_POINTER, + G_TYPE_BOOLEAN, 3, + G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + e_cert_db_signals[PK11_CHANGE_PASSWD] = g_signal_new ( + "pk11_change_passwd", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, pk11_change_passwd), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_POINTER, + G_TYPE_BOOLEAN, 2, + G_TYPE_POINTER, G_TYPE_POINTER); + + e_cert_db_signals[CONFIRM_CA_CERT_IMPORT] = g_signal_new ( + "confirm_ca_cert_import", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ECertDBClass, confirm_ca_cert_import), + NULL, NULL, + e_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 4, + G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); +} + +static void +e_cert_db_init (ECertDB *ec) +{ +} + +GMutex init_mutex; +static ECertDB *cert_db = NULL; + +ECertDB * +e_cert_db_peek (void) +{ + g_mutex_lock (&init_mutex); + if (!cert_db) + cert_db = g_object_new (E_TYPE_CERT_DB, NULL); + g_mutex_unlock (&init_mutex); + + return cert_db; +} + +void +e_cert_db_shutdown (void) +{ + /* XXX */ +} + +/* searching for certificates */ +ECert * +e_cert_db_find_cert_by_nickname (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + CERTCertificate *cert = NULL; + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting \"%s\"\n", asciiname));*/ + cert = PK11_FindCertFromNickname ((gchar *) nickname, NULL); + if (!cert) { + cert = CERT_FindCertByNickname (CERT_GetDefaultCertDB (), (gchar *) nickname); + } + + if (cert) { + /* PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("got it\n"));*/ + ECert *ecert = e_cert_new (cert); + return ecert; + } + else { + set_nss_error (error); + return NULL; + } +} + +#ifdef notyet +ECert * +e_cert_db_find_cert_by_key (ECertDB *certdb, + const gchar *db_key, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + SECItem keyItem = {siBuffer, NULL, 0}; + SECItem *dummy; + CERTIssuerAndSN issuerSN; + gulong moduleID,slotID; + CERTCertificate *cert; + + if (!db_key) { + set_nss_error (error); + return NULL; + } + + dummy = NSSBase64_DecodeBuffer ( + NULL, &keyItem, db_key, + (PRUint32) PL_strlen (db_key)); + + /* someday maybe we can speed up the search using the moduleID and slotID*/ + moduleID = NS_NSS_GET_LONG (keyItem.data); + slotID = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG]); + + /* build the issuer/SN structure*/ + issuerSN.serialNumber.len = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG *2]); + issuerSN.derIssuer.len = NS_NSS_GET_LONG (&keyItem.data[NS_NSS_LONG *3]); + issuerSN.serialNumber.data= &keyItem.data[NS_NSS_LONG *4]; + issuerSN.derIssuer.data= &keyItem.data[NS_NSS_LONG *4+ + issuerSN.serialNumber.len]; + + cert = CERT_FindCertByIssuerAndSN (CERT_GetDefaultCertDB (), &issuerSN); + PR_FREEIF (keyItem.data); + if (cert) { + ECert *ecert = e_cert_new (cert); + return e_cert; + } + + set_nss_error (error); + return NULL; +} + +GList * +e_cert_db_get_cert_nicknames (ECertDB *certdb, + ECertType cert_type, + GError **error) +{ +} + +ECert * +e_cert_db_find_email_encryption_cert (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ +} + +ECert * +e_cert_db_find_email_signing_cert (ECertDB *certdb, + const gchar *nickname, + GError **error) +{ +} +#endif + +ECert * +e_cert_db_find_cert_by_email_address (ECertDB *certdb, + const gchar *email, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker; */ + ECert *cert; + CERTCertificate *any_cert; + CERTCertList *certlist; + + any_cert = CERT_FindCertByNicknameOrEmailAddr ( + CERT_GetDefaultCertDB (), (gchar *) email); + + if (!any_cert) { + set_nss_error (error); + return NULL; + } + + /* any_cert now contains a cert with the right subject, + * but it might not have the correct usage. */ + certlist = CERT_CreateSubjectCertList ( + NULL, + CERT_GetDefaultCertDB (), + &any_cert->derSubject, + PR_Now (), PR_TRUE); + if (!certlist) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + return NULL; + } + + if (SECSuccess != CERT_FilterCertListByUsage ( + certlist, certUsageEmailRecipient, PR_FALSE)) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + CERT_DestroyCertList (certlist); + return NULL; + } + + if (CERT_LIST_END (CERT_LIST_HEAD (certlist), certlist)) { + set_nss_error (error); + CERT_DestroyCertificate (any_cert); + CERT_DestroyCertList (certlist); + return NULL; + } + + cert = e_cert_new (CERT_DupCertificate (CERT_LIST_HEAD (certlist)->cert)); + + CERT_DestroyCertList (certlist); + CERT_DestroyCertificate (any_cert); + + return cert; +} + +static gboolean +confirm_download_ca_cert (ECertDB *cert_db, + ECert *cert, + gboolean *trust_ssl, + gboolean *trust_email, + gboolean *trust_objsign) +{ + gboolean rv = FALSE; + + *trust_ssl = + *trust_email = + *trust_objsign = FALSE; + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[CONFIRM_CA_CERT_IMPORT], 0, + cert, + trust_ssl, + trust_email, + trust_objsign, + &rv); + + return rv; +} + +static gboolean +handle_ca_cert_download (ECertDB *cert_db, + GList *certs, + GError **error) +{ + ECert *certToShow; + SECItem der; + gchar *raw_der = NULL; + CERTCertificate *tmpCert; + + /* First thing we have to do is figure out which certificate + * we're gonna present to the user. The CA may have sent down + * a list of certs which may or may not be a chained list of + * certs. Until the day we can design some solid UI for the + * general case, we'll code to the > 90% case. That case is + * where a CA sends down a list that is a chain up to its root + * in either ascending or descending order. What we're gonna + * do is compare the first 2 entries, if the first was signed + * by the second, we assume the leaf cert is the first cert + * and display it. If the second cert was signed by the first + * cert, then we assume the first cert is the root and the + * last cert in the array is the leaf. In this case we + * display the last cert. + */ + + /* nsNSSShutDownPreventionLock locker;*/ + + if (certs == NULL) { + g_warning ("Didn't get any certs to import."); + return TRUE; + } + else if (certs->next == NULL) { + /* there's 1 cert */ + certToShow = E_CERT (certs->data); + } + else { + /* there are multiple certs */ + ECert *cert0; + ECert *cert1; + const gchar * cert0SubjectName; + const gchar * cert0IssuerName; + const gchar * cert1SubjectName; + const gchar * cert1IssuerName; + + cert0 = E_CERT (certs->data); + cert1 = E_CERT (certs->next->data); + + cert0IssuerName = e_cert_get_issuer_name (cert0); + cert0SubjectName = e_cert_get_subject_name (cert0); + + cert1IssuerName = e_cert_get_issuer_name (cert1); + cert1SubjectName = e_cert_get_subject_name (cert1); + + if (!strcmp (cert1IssuerName, cert0SubjectName)) { + /* In this case, the first cert in the list signed the second, + * so the first cert is the root. Let's display the last cert + * in the list. */ + certToShow = E_CERT (g_list_last (certs)->data); + } + else if (!strcmp (cert0IssuerName, cert1SubjectName)) { + /* In this case the second cert has signed the first cert. The + * first cert is the leaf, so let's display it. */ + certToShow = cert0; + } else { + /* It's not a chain, so let's just show the first one in the + * downloaded list. */ + certToShow = cert0; + } + } + + if (!certToShow) { + set_nss_error (error); + return FALSE; + } + + if (!e_cert_get_raw_der (certToShow, &raw_der, &der.len)) { + set_nss_error (error); + return FALSE; + } + + der.data = (guchar *) raw_der; + + { + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));*/ + CERTCertDBHandle *certdb = CERT_GetDefaultCertDB (); + tmpCert = CERT_FindCertByDERCert (certdb, &der); + if (!tmpCert) { + tmpCert = CERT_NewTempCertificate ( + certdb, &der, + NULL, PR_FALSE, PR_TRUE); + } + if (!tmpCert) { + g_warning ("Couldn't create cert from DER blob"); + set_nss_error (error); + return FALSE; + } + } + +#if 0 + CERTCertificateCleaner tmpCertCleaner (tmpCert); +#endif + + if (tmpCert->isperm) { + if (error && !*error) + *error = g_error_new_literal (E_CERTDB_ERROR, 0, _("Certificate already exists")); + return FALSE; + } + else { + gboolean trust_ssl, trust_email, trust_objsign; + gchar *nickname; + SECStatus srv; + CERTCertTrust trust; + + if (!confirm_download_ca_cert ( + cert_db, certToShow, &trust_ssl, + &trust_email, &trust_objsign)) { + set_nss_error (error); + return FALSE; + } + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits));*/ + + nickname = CERT_MakeCANickname (tmpCert); + + /*PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));*/ + + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trust_add_ca_trust ( + &trust, + trust_ssl, + trust_email, + trust_objsign); + + srv = CERT_AddTempCertToPerm ( + tmpCert, + nickname, + &trust); + + /* If we aren't logged into the token, then what *should* + * happen is the above call should fail, and we should + * authenticate and then try again. But see NSS bug #595861. + * With NSS 3.12.6 at least, the above call will fail, but + * it *will* have added the cert to the database, with + * random trust bits. We have to authenticate and then set + * the trust bits correctly. And calling + * CERT_AddTempCertToPerm() again doesn't work either -- it'll + * fail even though it arguably ought to succeed (which is + * probably another NSS bug). + * So if we get SEC_ERROR_TOKEN_NOT_LOGGED_IN, we first try + * CERT_ChangeCertTrust(), and if that doesn't work we hope + * we're on a fixed version of NSS and we try calling + * CERT_AddTempCertToPerm() again instead. */ + if (srv != SECSuccess && + PORT_GetError () == SEC_ERROR_TOKEN_NOT_LOGGED_IN && + e_cert_db_login_to_slot (NULL, PK11_GetInternalKeySlot ())) { + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + tmpCert, &trust); + if (srv != SECSuccess) + srv = CERT_AddTempCertToPerm ( + tmpCert, + nickname, + &trust); + } + if (srv != SECSuccess) { + set_nss_error (error); + return FALSE; + } + +#if 0 + /* Now it's time to add the rest of the certs we just downloaded. + * Since we didn't prompt the user about any of these certs, we + * won't set any trust bits for them. */ + e_cert_trust_init (&trust); + e_cert_trust_set_valid_ca (&trust); + e_cert_trusts_add_ca_trust (&trust, 0, 0, 0); + for (PRUint32 i = 0; i < numCerts; i++) { + if (i == selCertIndex) + continue; + + certToShow = do_QueryElementAt (x509Certs, i); + certToShow->GetRawDER (&der.len, (PRUint8 **) &der.data); + + CERTCertificate *tmpCert2 = + CERT_NewTempCertificate (certdb, &der, nsnull, PR_FALSE, PR_TRUE); + + if (!tmpCert2) { + NS_ASSERTION (0, "Couldn't create temp cert from DER blob\n"); + continue; /* Let's try to import the rest of 'em */ + } + nickname.Adopt (CERT_MakeCANickname (tmpCert2)); + CERT_AddTempCertToPerm ( + tmpCert2, NS_CONST_CAST (gchar *,nickname.get ()), + defaultTrust.GetTrust ()); + CERT_DestroyCertificate (tmpCert2); + } +#endif + return TRUE; + } +} +gboolean e_cert_db_change_cert_trust (CERTCertificate *cert, CERTCertTrust *trust) +{ + SECStatus srv; + + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + cert, trust); + if (srv != SECSuccess && + PORT_GetError () == SEC_ERROR_TOKEN_NOT_LOGGED_IN && + e_cert_db_login_to_slot (NULL, PK11_GetInternalKeySlot ())) + srv = CERT_ChangeCertTrust ( + CERT_GetDefaultCertDB (), + cert, trust); + + if (srv != SECSuccess) { + glong err = PORT_GetError (); + g_warning ( + "CERT_ChangeCertTrust() failed: %s\n", + nss_error_to_string (err)); + return FALSE; + } + return TRUE; +} + +/* deleting certificates */ +gboolean +e_cert_db_delete_cert (ECertDB *certdb, + ECert *ecert) +{ + /* nsNSSShutDownPreventionLock locker; + * nsNSSCertificate *nssCert = NS_STATIC_CAST (nsNSSCertificate *, aCert); */ + + CERTCertificate *cert; + + if (!e_cert_mark_for_deletion (ecert)) { + return FALSE; + } + + cert = e_cert_get_internal_cert (ecert); + if (cert->slot && e_cert_get_cert_type (ecert) != E_CERT_USER) { + /* To delete a cert of a slot (builtin, most likely), mark it as + * completely untrusted. This way we keep a copy cached in the + * local database, and next time we try to load it off of the + * external token/slot, we'll know not to trust it. We don't + * want to do that with user certs, because a user may re-store + * the cert onto the card again at which point we *will* want to + * trust that cert if it chains up properly. */ + CERTCertTrust trust; + + e_cert_trust_init_with_values (&trust, 0, 0, 0); + return e_cert_db_change_cert_trust (cert, &trust); + } + + return TRUE; +} + +/* importing certificates */ +gboolean +e_cert_db_import_certs (ECertDB *certdb, + gchar *data, + guint32 length, + ECertType cert_type, + GSList **imported_certs, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + PRArenaPool *arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + GList *certs = NULL; + CERTDERCerts *certCollection = e_cert_db_get_certs_from_package (arena, data, length); + gint i; + gboolean rv; + + if (!certCollection) { + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + + /* Now let's create some certs to work with */ + for (i = 0; i < certCollection->numcerts; i++) { + SECItem *currItem = &certCollection->rawCerts[i]; + ECert *cert; + + cert = e_cert_new_from_der ((gchar *) currItem->data, currItem->len); + if (!cert) { + set_nss_error (error); + g_list_foreach (certs, (GFunc) g_object_unref, NULL); + g_list_free (certs); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + certs = g_list_append (certs, cert); + } + switch (cert_type) { + case E_CERT_CA: + rv = handle_ca_cert_download (certdb, certs, error); + if (rv && imported_certs) { + GList *l; + + /* copy certificates to the caller */ + *imported_certs = NULL; + for (l = certs; l; l = l->next) { + ECert *cert = l->data; + + if (cert) + *imported_certs = g_slist_prepend (*imported_certs, g_object_ref (cert)); + } + + *imported_certs = g_slist_reverse (*imported_certs); + } + break; + default: + /* We only deal with import CA certs in this method currently.*/ + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + rv = FALSE; + } + + g_list_foreach (certs, (GFunc) g_object_unref, NULL); + g_list_free (certs); + PORT_FreeArena (arena, PR_FALSE); + return rv; +} + +gboolean +e_cert_db_import_email_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GSList **imported_certs, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + SECStatus srv = SECFailure; + gboolean rv = TRUE; + CERTCertificate * cert; + SECItem **rawCerts; + gint numcerts; + gint i; + PRArenaPool *arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + CERTDERCerts *certCollection = e_cert_db_get_certs_from_package (arena, data, length); + + if (!certCollection) { + set_nss_error (error); + PORT_FreeArena (arena, PR_FALSE); + return FALSE; + } + + cert = CERT_NewTempCertificate ( + CERT_GetDefaultCertDB (), certCollection->rawCerts, + (gchar *) NULL, PR_FALSE, PR_TRUE); + if (!cert) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + numcerts = certCollection->numcerts; + rawCerts = (SECItem **) PORT_Alloc (sizeof (SECItem *) * numcerts); + if (!rawCerts) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + + for (i = 0; i < numcerts; i++) { + rawCerts[i] = &certCollection->rawCerts[i]; + } + + srv = CERT_ImportCerts ( + CERT_GetDefaultCertDB (), certUsageEmailSigner, + numcerts, rawCerts, NULL, PR_TRUE, PR_FALSE, + NULL); + if (srv != SECSuccess) { + set_nss_error (error); + rv = FALSE; + goto loser; + } + CERT_SaveSMimeProfile (cert, NULL, NULL); + + if (imported_certs) { + *imported_certs = NULL; + for (i = 0; i < certCollection->numcerts; i++) { + SECItem *currItem = &certCollection->rawCerts[i]; + ECert *cert; + + cert = e_cert_new_from_der ((gchar *) currItem->data, currItem->len); + if (cert) + *imported_certs = g_slist_prepend (*imported_certs, cert); + } + + *imported_certs = g_slist_reverse (*imported_certs); + } + + PORT_Free (rawCerts); + loser: + if (cert) + CERT_DestroyCertificate (cert); + if (arena) + PORT_FreeArena (arena, PR_TRUE); + return rv; +} + +static gchar * +default_nickname (CERTCertificate *cert) +{ + /* nsNSSShutDownPreventionLock locker; */ + gchar *username = NULL; + gchar *caname = NULL; + gchar *nickname = NULL; + gchar *tmp = NULL; + gint count; + const gchar *nickFmt = NULL; + CERTCertificate *dummycert; + PK11SlotInfo *slot = NULL; + CK_OBJECT_HANDLE keyHandle; + + CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB (); + + username = CERT_GetCommonName (&cert->subject); + if (username == NULL) + username = PL_strdup (""); + + if (username == NULL) + goto loser; + + caname = CERT_GetOrgName (&cert->issuer); + if (caname == NULL) + caname = PL_strdup (""); + + if (caname == NULL) + goto loser; + + count = 1; + + nickFmt = "%1$s's %2$s ID"; + + nickname = PR_smprintf (nickFmt, username, caname); + /* + * We need to see if the private key exists on a token, if it does + * then we need to check for nicknames that already exist on the smart + * card. + */ + slot = PK11_KeyForCertExists (cert, &keyHandle, NULL); + if (slot == NULL) { + goto loser; + } + if (!PK11_IsInternal (slot)) { + tmp = PR_smprintf ("%s:%s", PK11_GetTokenName (slot), nickname); + PR_Free (nickname); + nickname = tmp; + tmp = NULL; + } + tmp = nickname; + while (1) { + if (count > 1) { + nickname = PR_smprintf ("%s #%d", tmp, count); + } + + if (nickname == NULL) + goto loser; + + if (PK11_IsInternal (slot)) { + /* look up the nickname to make sure it isn't in use already */ + dummycert = CERT_FindCertByNickname (defaultcertdb, nickname); + + } else { + /* + * Check the cert against others that already live on the smart + * card. + */ + dummycert = PK11_FindCertFromNickname (nickname, NULL); + if (dummycert != NULL) { + /* + * Make sure the subject names are different. + */ + if (CERT_CompareName (&cert->subject, &dummycert->subject) == SECEqual) { + /* + * There is another certificate with the same nickname and + * the same subject name on the smart card, so let's use this + * nickname. + */ + CERT_DestroyCertificate (dummycert); + dummycert = NULL; + } + } + } + if (dummycert == NULL) + goto done; + + /* found a cert, destroy it and loop */ + CERT_DestroyCertificate (dummycert); + if (tmp != nickname) PR_Free (nickname); + count++; + } /* end of while (1) */ + + loser: + if (nickname) { + PR_Free (nickname); + } + nickname = NULL; + done: + if (caname) { + PR_Free (caname); + } + if (username) { + PR_Free (username); + } + if (slot != NULL) { + PK11_FreeSlot (slot); + if (nickname != NULL) { + tmp = nickname; + nickname = strchr (tmp, ':'); + if (nickname != NULL) { + nickname++; + nickname = PL_strdup (nickname); + PR_Free (tmp); + tmp = NULL; + } else { + nickname = tmp; + tmp = NULL; + } + } + } + PR_FREEIF (tmp); + return (nickname); +} + +gboolean +e_cert_db_import_user_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker;*/ + PK11SlotInfo *slot; + gchar * nickname = NULL; + gboolean rv = FALSE; + gint numCACerts; + SECItem *CACerts; + CERTDERCerts * collectArgs; + PRArenaPool *arena; + CERTCertificate * cert = NULL; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + set_nss_error (error); + goto loser; + } + + collectArgs = e_cert_db_get_certs_from_package (arena, data, length); + if (!collectArgs) { + set_nss_error (error); + goto loser; + } + + cert = CERT_NewTempCertificate ( + CERT_GetDefaultCertDB (), collectArgs->rawCerts, + (gchar *) NULL, PR_FALSE, PR_TRUE); + if (!cert) { + set_nss_error (error); + goto loser; + } + + slot = PK11_KeyForCertExists (cert, NULL, NULL); + if (slot == NULL) { + set_nss_error (error); + goto loser; + } + PK11_FreeSlot (slot); + + /* pick a nickname for the cert */ + if (cert->nickname) { + /* sigh, we need a call to look up other certs with this subject and + * identify nicknames from them. We can no longer walk down internal + * database structures rjr */ + nickname = cert->nickname; + } + else { + nickname = default_nickname (cert); + } + + /* user wants to import the cert */ + slot = PK11_ImportCertForKey (cert, nickname, NULL); + if (!slot) { + set_nss_error (error); + goto loser; + } + PK11_FreeSlot (slot); + numCACerts = collectArgs->numcerts - 1; + + if (numCACerts) { + CACerts = collectArgs->rawCerts + 1; + if (!CERT_ImportCAChain (CACerts, numCACerts, certUsageUserCertImport)) { + rv = TRUE; + } + } + + loser: + if (arena) { + PORT_FreeArena (arena, PR_FALSE); + } + if (cert) { + CERT_DestroyCertificate (cert); + } + return rv; +} + +gboolean +e_cert_db_import_server_cert (ECertDB *certdb, + gchar *data, + guint32 length, + GSList **imported_certs, + GError **error) +{ + /* not c&p'ing this over at the moment, as we don't have a UI + * for server certs anyway */ + return FALSE; +} + +gboolean +e_cert_db_import_certs_from_file (ECertDB *cert_db, + const gchar *file_path, + ECertType cert_type, + GSList **imported_certs, + GError **error) +{ + gboolean rv; + gint fd; + struct stat sb; + gchar *buf; + gint bytes_read; + + switch (cert_type) { + case E_CERT_CA: + case E_CERT_CONTACT: + case E_CERT_SITE: + /* good */ + break; + + default: + /* not supported (yet) */ + set_nss_error (error); + return FALSE; + } + + fd = g_open (file_path, O_RDONLY | O_BINARY, 0); + if (fd == -1) { + set_nss_error (error); + return FALSE; + } + + if (-1 == fstat (fd, &sb)) { + set_nss_error (error); + close (fd); + return FALSE; + } + + buf = g_malloc (sb.st_size); + if (!buf) { + set_nss_error (error); + close (fd); + return FALSE; + } + + bytes_read = read (fd, buf, sb.st_size); + + close (fd); + + if (bytes_read != sb.st_size) { + set_nss_error (error); + rv = FALSE; + } + else { + printf ("importing %d bytes from '%s'\n", bytes_read, file_path); + + switch (cert_type) { + case E_CERT_CA: + rv = e_cert_db_import_certs (cert_db, buf, bytes_read, cert_type, imported_certs, error); + break; + + case E_CERT_SITE: + rv = e_cert_db_import_server_cert (cert_db, buf, bytes_read, imported_certs, error); + break; + + case E_CERT_CONTACT: + rv = e_cert_db_import_email_cert (cert_db, buf, bytes_read, imported_certs, error); + break; + + default: + rv = FALSE; + break; + } + } + + g_free (buf); + return rv; +} + +gboolean +e_cert_db_import_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GError **error) +{ + EPKCS12 *pkcs12 = e_pkcs12_new (); + GError *e = NULL; + + if (!e_pkcs12_import_from_file (pkcs12, file_path, &e)) { + g_propagate_error (error, e); + return FALSE; + } + + return TRUE; +} + +#ifdef notyet +gboolean +e_cert_db_export_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GList *certs, + GError **error) +{ +} +#endif + +gboolean +e_cert_db_login_to_slot (ECertDB *cert_db, + PK11SlotInfo *slot) +{ + if (PK11_NeedLogin (slot)) { + PK11_Logout (slot); + + if (PK11_NeedUserInit (slot)) { + gchar *pwd; + gboolean rv = FALSE; + + printf ("initializing slot password\n"); + + g_signal_emit ( + e_cert_db_peek (), + e_cert_db_signals[PK11_CHANGE_PASSWD], 0, + NULL, + &pwd, + &rv); + + if (!rv) + return FALSE; + + /* the user needs to specify the initial password */ + PK11_InitPin (slot, "", pwd); + } + + PK11_SetPasswordFunc (pk11_password); + if (PK11_Authenticate (slot, PR_TRUE, NULL) != SECSuccess) { + printf ( + "PK11_Authenticate failed (err = %d/%d)\n", + PORT_GetError (), PORT_GetError () + 0x2000); + return FALSE; + } + } + + return TRUE; +} diff --git a/smime/lib/e-cert-db.h b/smime/lib/e-cert-db.h new file mode 100644 index 0000000000..26d0a25c2a --- /dev/null +++ b/smime/lib/e-cert-db.h @@ -0,0 +1,148 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _E_CERT_DB_H_ +#define _E_CERT_DB_H_ + +#include <glib-object.h> +#include "e-cert.h" +#include <cert.h> + +#define E_TYPE_CERT_DB (e_cert_db_get_type ()) +#define E_CERT_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CERT_DB, ECertDB)) +#define E_CERT_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CERT_DB, ECertDBClass)) +#define E_IS_CERT_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CERT_DB)) +#define E_IS_CERT_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CERT_DB)) +#define E_CERT_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_CERT_DB, ECertDBClass)) + +#define E_CERTDB_ERROR e_certdb_error_quark() +GQuark e_certdb_error_quark (void) G_GNUC_CONST; + +typedef struct _ECertDB ECertDB; +typedef struct _ECertDBClass ECertDBClass; +typedef struct _ECertDBPrivate ECertDBPrivate; + +struct _ECertDB { + GObject parent; + + ECertDBPrivate *priv; +}; + +struct _ECertDBClass { + GObjectClass parent_class; + + /* signals */ + gboolean (*pk11_passwd) (ECertDB *db, PK11SlotInfo *slot, gboolean retry, gchar **passwd); + gboolean (*pk11_change_passwd) (ECertDB *db, gchar **orig_passwd, gchar **passwd); + gboolean (*confirm_ca_cert_import) (ECertDB *db, ECert *cert, gboolean *trust_ssl, gboolean *trust_email, gboolean *trust_objsign); + + /* Padding for future expansion */ + void (*_ecert_reserved0) (void); + void (*_ecert_reserved1) (void); + void (*_ecert_reserved2) (void); + void (*_ecert_reserved3) (void); + void (*_ecert_reserved4) (void); +}; + +GType e_cert_db_get_type (void); + +/* single instance */ +ECertDB * e_cert_db_peek (void); + +void e_cert_db_shutdown (void); + +/* searching for certificates */ +ECert * e_cert_db_find_cert_by_nickname (ECertDB *certdb, + const gchar *nickname, + GError **error); + +#ifdef notyet +ECert * e_cert_db_find_cert_by_key (ECertDB *certdb, + const gchar *db_key, + GError **error); + +GList * e_cert_db_get_cert_nicknames (ECertDB *certdb, + ECertType cert_type, + GError **error); + +ECert * e_cert_db_find_email_encryption_cert (ECertDB *certdb, + const gchar *nickname, + GError **error); + +ECert * e_cert_db_find_email_signing_cert (ECertDB *certdb, + const gchar *nickname, + GError **error); +#endif + +ECert * e_cert_db_find_cert_by_email_address (ECertDB *certdb, + const gchar *nickname, + GError **error); + +/* deleting certificates */ +gboolean e_cert_db_delete_cert (ECertDB *certdb, + ECert *cert); + +/* importing certificates */ +gboolean e_cert_db_import_certs (ECertDB *certdb, + gchar *data, guint32 length, + ECertType cert_type, + GSList **imported_certs, + GError **error); + +gboolean e_cert_db_import_email_cert (ECertDB *certdb, + gchar *data, guint32 length, + GSList **imported_certs, + GError **error); + +gboolean e_cert_db_import_user_cert (ECertDB *certdb, + gchar *data, guint32 length, + GError **error); + +gboolean e_cert_db_import_server_cert (ECertDB *certdb, + gchar *data, guint32 length, + GSList **imported_certs, + GError **error); + +gboolean e_cert_db_import_certs_from_file (ECertDB *cert_db, + const gchar *file_path, + ECertType cert_type, + GSList **imported_certs, + GError **error); + +gboolean e_cert_db_import_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GError **error); + +#ifdef notyet +gboolean e_cert_db_export_pkcs12_file (ECertDB *cert_db, + const gchar *file_path, + GList *certs, + GError **error); +#endif + +gboolean e_cert_db_login_to_slot (ECertDB *cert_db, + PK11SlotInfo *slot); + +gboolean e_cert_db_change_cert_trust (CERTCertificate *cert, + CERTCertTrust *trust); + +#endif /* _E_CERT_DB_H_ */ diff --git a/smime/lib/e-cert-trust.c b/smime/lib/e-cert-trust.c new file mode 100644 index 0000000000..e99c5c4047 --- /dev/null +++ b/smime/lib/e-cert-trust.c @@ -0,0 +1,471 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "e-cert-trust.h" + +void +e_cert_trust_init (CERTCertTrust *trust) +{ + memset (trust, 0, sizeof (CERTCertTrust)); +} + +void +e_cert_trust_init_with_values (CERTCertTrust *trust, + guint ssl, + guint email, + guint objsign) +{ + memset (trust, 0, sizeof (CERTCertTrust)); + e_cert_trust_add_trust (&trust->sslFlags, ssl); + e_cert_trust_add_trust (&trust->emailFlags, email); + e_cert_trust_add_trust (&trust->objectSigningFlags, objsign); +} + +void +e_cert_trust_copy (CERTCertTrust *trust, + CERTCertTrust *t) +{ + if (t) + memcpy (trust, t, sizeof (CERTCertTrust)); + else + memset (trust, 0, sizeof (CERTCertTrust)); +} + +void +e_cert_trust_add_ca_trust (CERTCertTrust *trust, + PRBool ssl, + PRBool email, + PRBool objSign) +{ + if (ssl) { + e_cert_trust_add_trust ( + &trust->sslFlags, CERTDB_TRUSTED_CA); + e_cert_trust_add_trust ( + &trust->sslFlags, CERTDB_TRUSTED_CLIENT_CA); + } + + if (email) { + e_cert_trust_add_trust ( + &trust->emailFlags, CERTDB_TRUSTED_CA); + e_cert_trust_add_trust ( + &trust->emailFlags, CERTDB_TRUSTED_CLIENT_CA); + } + + if (objSign) { + e_cert_trust_add_trust ( + &trust->objectSigningFlags, CERTDB_TRUSTED_CA); + e_cert_trust_add_trust ( + &trust->objectSigningFlags, CERTDB_TRUSTED_CLIENT_CA); + } +} + +void +e_cert_trust_add_peer_trust (CERTCertTrust *trust, + PRBool ssl, + PRBool email, + PRBool objSign) +{ + if (ssl) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_TRUSTED); + if (email) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_TRUSTED); + if (objSign) + e_cert_trust_add_trust (&trust->objectSigningFlags, CERTDB_TRUSTED); +} + +void +e_cert_trust_set_ssl_trust (CERTCertTrust *trust, + PRBool peer, + PRBool tPeer, + PRBool ca, + PRBool tCA, + PRBool tClientCA, + PRBool user, + PRBool warn) +{ + trust->sslFlags = 0; + if (peer || tPeer) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_TERMINAL_RECORD); + if (tPeer) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_TRUSTED); + if (ca || tCA) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_VALID_CA); + if (tClientCA) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_TRUSTED_CLIENT_CA); + if (tCA) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_TRUSTED_CA); + if (user) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_USER); + if (warn) + e_cert_trust_add_trust (&trust->sslFlags, CERTDB_SEND_WARN); +} + +void +e_cert_trust_set_email_trust (CERTCertTrust *trust, + PRBool peer, + PRBool tPeer, + PRBool ca, + PRBool tCA, + PRBool tClientCA, + PRBool user, + PRBool warn) +{ + trust->emailFlags = 0; + if (peer || tPeer) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_TERMINAL_RECORD); + if (tPeer) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_TRUSTED); + if (ca || tCA) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_VALID_CA); + if (tClientCA) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_TRUSTED_CLIENT_CA); + if (tCA) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_TRUSTED_CA); + if (user) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_USER); + if (warn) + e_cert_trust_add_trust (&trust->emailFlags, CERTDB_SEND_WARN); +} + +void +e_cert_trust_set_objsign_trust (CERTCertTrust *trust, + PRBool peer, + PRBool tPeer, + PRBool ca, + PRBool tCA, + PRBool tClientCA, + PRBool user, + PRBool warn) +{ + trust->objectSigningFlags = 0; + if (peer || tPeer) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_TERMINAL_RECORD); + if (tPeer) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_TRUSTED); + if (ca || tCA) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_VALID_CA); + if (tClientCA) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_TRUSTED_CLIENT_CA); + if (tCA) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_TRUSTED_CA); + if (user) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_USER); + if (warn) + e_cert_trust_add_trust ( + &trust->objectSigningFlags, + CERTDB_SEND_WARN); +} + +void +e_cert_trust_set_valid_ca (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_trusted_server_ca (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_trusted_ca (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_TRUE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_TRUE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_FALSE, PR_FALSE, PR_TRUE, + PR_TRUE, PR_TRUE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_valid_peer (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_TRUE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_TRUE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_TRUE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_valid_server_peer (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_TRUE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_FALSE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_FALSE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_trusted_peer (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_TRUE, PR_TRUE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_TRUE, PR_TRUE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_TRUE, PR_TRUE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE); +} + +void +e_cert_trust_set_user (CERTCertTrust *trust) +{ + e_cert_trust_set_ssl_trust ( + trust, PR_FALSE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE); + + e_cert_trust_set_email_trust ( + trust, PR_FALSE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE); + + e_cert_trust_set_objsign_trust ( + trust, PR_FALSE, PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, PR_TRUE, PR_FALSE); +} + +PRBool +e_cert_trust_has_any_ca (CERTCertTrust *trust) +{ + if (e_cert_trust_has_trust (trust->sslFlags, CERTDB_VALID_CA) || + e_cert_trust_has_trust (trust->emailFlags, CERTDB_VALID_CA) || + e_cert_trust_has_trust (trust->objectSigningFlags, CERTDB_VALID_CA)) + return PR_TRUE; + + return PR_FALSE; +} + +PRBool +e_cert_trust_has_ca (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign) +{ + if (checkSSL && !e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_VALID_CA)) + return PR_FALSE; + + if (checkEmail && !e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_VALID_CA)) + return PR_FALSE; + + if (checkObjSign && !e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_VALID_CA)) + return PR_FALSE; + + return PR_TRUE; +} + +PRBool +e_cert_trust_has_peer (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign) +{ + if (checkSSL && !e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_TERMINAL_RECORD)) + return PR_FALSE; + + if (checkEmail && !e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_TERMINAL_RECORD)) + return PR_FALSE; + + if (checkObjSign && !e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_TERMINAL_RECORD)) + return PR_FALSE; + + return PR_TRUE; +} + +PRBool +e_cert_trust_has_any_user (CERTCertTrust *trust) +{ + if (e_cert_trust_has_trust (trust->sslFlags, CERTDB_USER) || + e_cert_trust_has_trust (trust->emailFlags, CERTDB_USER) || + e_cert_trust_has_trust (trust->objectSigningFlags, CERTDB_USER)) + return PR_TRUE; + + return PR_FALSE; +} + +PRBool +e_cert_trust_has_user (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign) +{ + if (checkSSL && !e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_USER)) + return PR_FALSE; + + if (checkEmail && !e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_USER)) + return PR_FALSE; + + if (checkObjSign && !e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_USER)) + return PR_FALSE; + + return PR_TRUE; +} + +PRBool +e_cert_trust_has_trusted_ca (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign) +{ + if (checkSSL && !(e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_TRUSTED_CA) || + e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_TRUSTED_CLIENT_CA))) + return PR_FALSE; + + if (checkEmail && !(e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_TRUSTED_CA) || + e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_TRUSTED_CLIENT_CA))) + return PR_FALSE; + + if (checkObjSign && !(e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_TRUSTED_CA) || + e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_TRUSTED_CLIENT_CA))) + return PR_FALSE; + + return PR_TRUE; +} + +PRBool +e_cert_trust_has_trusted_peer (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign) +{ + if (checkSSL && !(e_cert_trust_has_trust ( + trust->sslFlags, CERTDB_TRUSTED))) + return PR_FALSE; + + if (checkEmail && !(e_cert_trust_has_trust ( + trust->emailFlags, CERTDB_TRUSTED))) + return PR_FALSE; + + if (checkObjSign && !(e_cert_trust_has_trust ( + trust->objectSigningFlags, CERTDB_TRUSTED))) + return PR_FALSE; + + return PR_TRUE; +} + +void +e_cert_trust_add_trust (guint *t, + guint v) +{ + *t |= v; +} + +PRBool +e_cert_trust_has_trust (guint t, + guint v) +{ + return (t & v); +} + diff --git a/smime/lib/e-cert-trust.h b/smime/lib/e-cert-trust.h new file mode 100644 index 0000000000..71608f8415 --- /dev/null +++ b/smime/lib/e-cert-trust.h @@ -0,0 +1,87 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _E_CERT_TRUST_H_ +#define _E_CERT_TRUST_H_ + +#include <glib.h> +#include <cert.h> +#include <certdb.h> + +G_BEGIN_DECLS + +void e_cert_trust_init (CERTCertTrust *trust); +void e_cert_trust_init_with_values (CERTCertTrust *trust, + guint ssl, + guint email, + guint objsign); +void e_cert_trust_copy (CERTCertTrust *dst_trust, CERTCertTrust *src_trust); +void e_cert_trust_add_ca_trust (CERTCertTrust *trust, PRBool ssl, PRBool email, PRBool objSign); +void e_cert_trust_add_peer_trust (CERTCertTrust *trust, PRBool ssl, PRBool email, PRBool objSign); +void e_cert_trust_set_ssl_trust (CERTCertTrust *trust, + PRBool peer, PRBool tPeer, + PRBool ca, PRBool tCA, PRBool tClientCA, + PRBool user, PRBool warn); +void e_cert_trust_set_email_trust (CERTCertTrust *trust, + PRBool peer, PRBool tPeer, + PRBool ca, PRBool tCA, PRBool tClientCA, + PRBool user, PRBool warn); +void e_cert_trust_set_objsign_trust (CERTCertTrust *trust, + PRBool peer, PRBool tPeer, + PRBool ca, PRBool tCA, PRBool tClientCA, + PRBool user, PRBool warn); +void e_cert_trust_set_valid_ca (CERTCertTrust *trust); +void e_cert_trust_set_trusted_server_ca (CERTCertTrust *trust); +void e_cert_trust_set_trusted_ca (CERTCertTrust *trust); +void e_cert_trust_set_valid_peer (CERTCertTrust *trust); +void e_cert_trust_set_valid_server_peer (CERTCertTrust *trust); +void e_cert_trust_set_trusted_peer (CERTCertTrust *trust); +void e_cert_trust_set_user (CERTCertTrust *trust); +PRBool e_cert_trust_has_any_ca (CERTCertTrust *trust); +PRBool e_cert_trust_has_ca (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign); +PRBool e_cert_trust_has_peer (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign); +PRBool e_cert_trust_has_any_user (CERTCertTrust *trust); +PRBool e_cert_trust_has_user (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign); +PRBool e_cert_trust_has_trusted_ca (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign); +PRBool e_cert_trust_has_trusted_peer (CERTCertTrust *trust, + PRBool checkSSL, + PRBool checkEmail, + PRBool checkObjSign); +void e_cert_trust_add_trust (guint *t, guint v); +PRBool e_cert_trust_has_trust (guint t, guint v); + +G_END_DECLS + +#endif /* _E_CERT_H_ */ diff --git a/smime/lib/e-cert.c b/smime/lib/e-cert.c new file mode 100644 index 0000000000..969b43c9ed --- /dev/null +++ b/smime/lib/e-cert.c @@ -0,0 +1,542 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <time.h> + +#include <glib/gi18n.h> + +/* for e_utf8_strftime, what about e_time_format_time? */ +#include <e-util/e-util.h> + +#include "e-cert.h" +#include "e-cert-trust.h" +#include "pk11func.h" +#include "certdb.h" +#include "hasht.h" + +#define E_CERT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_CERT, ECertPrivate)) + +struct _ECertPrivate { + CERTCertificate *cert; + + /* pointers we cache since the nss implementation allocs the + * string */ + gchar *org_name; + gchar *org_unit_name; + gchar *cn; + + gchar *issuer_org_name; + gchar *issuer_org_unit_name; + gchar *issuer_cn; + + PRTime issued_on; + PRTime expires_on; + + gchar *issued_on_string; + gchar *expires_on_string; + + gchar *serial_number; + + gchar *usage_string; + + gchar *sha1_fingerprint; + gchar *md5_fingerprint; + + EASN1Object *asn1; + + gboolean delete; +}; + +G_DEFINE_TYPE (ECert, e_cert, G_TYPE_OBJECT) + +static void +e_cert_finalize (GObject *object) +{ + ECertPrivate *priv; + + priv = E_CERT_GET_PRIVATE (object); + + if (priv->org_name) + PORT_Free (priv->org_name); + if (priv->org_unit_name) + PORT_Free (priv->org_unit_name); + if (priv->cn) + PORT_Free (priv->cn); + + if (priv->issuer_org_name) + PORT_Free (priv->issuer_org_name); + if (priv->issuer_org_unit_name) + PORT_Free (priv->issuer_org_unit_name); + if (priv->issuer_cn) + PORT_Free (priv->issuer_cn); + + if (priv->issued_on_string) + PORT_Free (priv->issued_on_string); + if (priv->expires_on_string) + PORT_Free (priv->expires_on_string); + if (priv->serial_number) + PORT_Free (priv->serial_number); + + g_free (priv->usage_string); + + if (priv->sha1_fingerprint) + PORT_Free (priv->sha1_fingerprint); + if (priv->md5_fingerprint) + PORT_Free (priv->md5_fingerprint); + + if (priv->asn1) + g_object_unref (priv->asn1); + + if (priv->delete) { + printf ("attempting to delete cert marked for deletion\n"); + if (e_cert_get_cert_type (E_CERT (object)) == E_CERT_USER) { + PK11_DeleteTokenCertAndKey (priv->cert, NULL); + } else if (!PK11_IsReadOnly (priv->cert->slot)) { + /* If the list of built-ins does contain a non-removable + * copy of this certificate, our call will not remove + * the certificate permanently, but rather remove all trust. */ + SEC_DeletePermCertificate (priv->cert); + } + } + + if (priv->cert) + CERT_DestroyCertificate (priv->cert); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_cert_parent_class)->finalize (object); +} + +static void +e_cert_class_init (ECertClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ECertPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = e_cert_finalize; +} + +static void +e_cert_init (ECert *ec) +{ + ec->priv = E_CERT_GET_PRIVATE (ec); +} + +static void +e_cert_populate (ECert *cert) +{ + CERTCertificate *c = cert->priv->cert; + guchar fingerprint[20]; + SECItem fpItem; + + cert->priv->org_name = CERT_GetOrgName (&c->subject); + cert->priv->org_unit_name = CERT_GetOrgUnitName (&c->subject); + + cert->priv->issuer_org_name = CERT_GetOrgName (&c->issuer); + cert->priv->issuer_org_unit_name = CERT_GetOrgUnitName (&c->issuer); + + cert->priv->cn = CERT_GetCommonName (&c->subject); + cert->priv->issuer_cn = CERT_GetCommonName (&c->issuer); + + if (SECSuccess == CERT_GetCertTimes ( + c, &cert->priv->issued_on, &cert->priv->expires_on)) { + PRExplodedTime explodedTime; + struct tm exploded_tm; + gchar buf[32]; + + PR_ExplodeTime ( + cert->priv->issued_on, + PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof (buf), _("%d/%m/%Y"), &exploded_tm); + cert->priv->issued_on_string = g_strdup (buf); + + PR_ExplodeTime ( + cert->priv->expires_on, + PR_LocalTimeParameters, &explodedTime); + exploded_tm.tm_sec = explodedTime.tm_sec; + exploded_tm.tm_min = explodedTime.tm_min; + exploded_tm.tm_hour = explodedTime.tm_hour; + exploded_tm.tm_mday = explodedTime.tm_mday; + exploded_tm.tm_mon = explodedTime.tm_month; + exploded_tm.tm_year = explodedTime.tm_year - 1900; + e_utf8_strftime (buf, sizeof (buf), _("%d/%m/%Y"), &exploded_tm); + cert->priv->expires_on_string = g_strdup (buf); + } + + cert->priv->serial_number = CERT_Hexify (&cert->priv->cert->serialNumber, TRUE); + + memset (fingerprint, 0, sizeof fingerprint); + PK11_HashBuf ( + SEC_OID_SHA1, fingerprint, + cert->priv->cert->derCert.data, + cert->priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = SHA1_LENGTH; + cert->priv->sha1_fingerprint = CERT_Hexify (&fpItem, TRUE); + + memset (fingerprint, 0, sizeof fingerprint); + PK11_HashBuf ( + SEC_OID_MD5, fingerprint, + cert->priv->cert->derCert.data, + cert->priv->cert->derCert.len); + fpItem.data = fingerprint; + fpItem.len = MD5_LENGTH; + cert->priv->md5_fingerprint = CERT_Hexify (&fpItem, TRUE); +} + +ECert * +e_cert_new (CERTCertificate *cert) +{ + ECert *ecert = E_CERT (g_object_new (E_TYPE_CERT, NULL)); + + /* ECert owns a reference to the 'cert', which will be freed on ECert finalize */ + ecert->priv->cert = cert; + + e_cert_populate (ecert); + + return ecert; +} + +ECert * +e_cert_new_from_der (gchar *data, + guint32 len) +{ + CERTCertificate *cert = CERT_DecodeCertFromPackage (data, len); + + if (!cert) + return NULL; + + if (cert->dbhandle == NULL) + cert->dbhandle = CERT_GetDefaultCertDB (); + + return e_cert_new (cert); +} + +CERTCertificate * +e_cert_get_internal_cert (ECert *cert) +{ + /* XXX should this refcnt it? */ + return cert->priv->cert; +} + +gboolean +e_cert_get_raw_der (ECert *cert, + gchar **data, + guint32 *len) +{ + /* XXX do we really need to check if cert->priv->cert is NULL + * here? it should always be non - null if we have the + * ECert.. */ + if (cert->priv->cert) { + *data = (gchar *)cert->priv->cert->derCert.data; + *len = (guint32)cert->priv->cert->derCert.len; + return TRUE; + } + + *len = 0; + return FALSE; + +} + +const gchar * +e_cert_get_window_title (ECert *cert) +{ + if (cert->priv->cert->nickname) + return cert->priv->cert->nickname; + else if (cert->priv->cn) + return cert->priv->cn; + else + return cert->priv->cert->subjectName; +} + +const gchar * +e_cert_get_nickname (ECert *cert) +{ + return cert->priv->cert->nickname; +} + +const gchar * +e_cert_get_email (ECert *cert) +{ + return cert->priv->cert->emailAddr; +} + +const gchar * +e_cert_get_org (ECert *cert) +{ + return cert->priv->org_name; +} + +const gchar * +e_cert_get_org_unit (ECert *cert) +{ + return cert->priv->org_unit_name; +} + +const gchar * +e_cert_get_cn (ECert *cert) +{ + return cert->priv->cn; +} + +const gchar * +e_cert_get_issuer_name (ECert *cert) +{ + return cert->priv->cert->issuerName; +} + +const gchar * +e_cert_get_issuer_cn (ECert *cert) +{ + return cert->priv->issuer_cn; +} + +const gchar * +e_cert_get_issuer_org (ECert *cert) +{ + return cert->priv->issuer_org_name; +} + +const gchar * +e_cert_get_issuer_org_unit (ECert *cert) +{ + return cert->priv->issuer_org_unit_name; +} + +const gchar * +e_cert_get_subject_name (ECert *cert) +{ + return cert->priv->cert->subjectName; +} + +PRTime +e_cert_get_issued_on_time (ECert *cert) +{ + return cert->priv->issued_on; +} + +const gchar * +e_cert_get_issued_on (ECert *cert) +{ + return cert->priv->issued_on_string; +} + +PRTime +e_cert_get_expires_on_time (ECert *cert) +{ + return cert->priv->expires_on; +} + +const gchar * +e_cert_get_expires_on (ECert *cert) +{ + return cert->priv->expires_on_string; +} + +static struct { + gint bit; + const gchar *text; +} usageinfo[] = { + /* x509 certificate usage types */ + { certificateUsageEmailSigner, N_("Sign") }, + { certificateUsageEmailRecipient, N_("Encrypt") }, +}; + +const gchar * +e_cert_get_usage (ECert *cert) +{ + if (cert->priv->usage_string == NULL) { + gint i; + GString *str = g_string_new (""); + CERTCertificate *icert = e_cert_get_internal_cert (cert); + + for (i = 0; i < G_N_ELEMENTS (usageinfo); i++) { + if (icert->keyUsage & usageinfo[i].bit) { + if (str->len != 0) + g_string_append (str, ", "); + g_string_append (str, _(usageinfo[i].text)); + } + } + + cert->priv->usage_string = str->str; + g_string_free (str, FALSE); + } + + return cert->priv->usage_string; +} + +const gchar * +e_cert_get_serial_number (ECert *cert) +{ + return cert->priv->serial_number; +} + +const gchar * +e_cert_get_sha1_fingerprint (ECert *cert) +{ + return cert->priv->sha1_fingerprint; +} + +const gchar * +e_cert_get_md5_fingerprint (ECert *cert) +{ + return cert->priv->md5_fingerprint; +} + +GList * +e_cert_get_issuers_chain (ECert *ecert) +{ + GList *issuers = NULL; + + while (ecert) { + CERTCertificate *cert = e_cert_get_internal_cert (ecert); + CERTCertificate *next_cert; + + if (SECITEM_CompareItem (&cert->derIssuer, &cert->derSubject) == SECEqual) + break; + + next_cert = CERT_FindCertIssuer (cert, PR_Now (), certUsageSSLClient); + if (!next_cert) + break; + + /* next_cert has a reference already */ + ecert = e_cert_new (next_cert); + + if (ecert) { + /* the first is issuer of the original ecert */ + issuers = g_list_append (issuers, ecert); + } + } + + return issuers; +} + +ECert * +e_cert_get_ca_cert (ECert *ecert) +{ + CERTCertificate *cert, *next = e_cert_get_internal_cert (ecert), *internal; + + cert = next; + internal = cert; + do { + if (cert != next && cert != internal) + CERT_DestroyCertificate (cert); + + cert = next; + next = CERT_FindCertIssuer (cert, PR_Now (), certUsageAnyCA); + } while (next && next != cert); + + if (cert == internal) + return g_object_ref (ecert); + else + return e_cert_new (cert); +} + +EASN1Object * +e_cert_get_asn1_struct (ECert *cert) +{ + if (!cert->priv->asn1) + cert->priv->asn1 = e_asn1_object_new_from_cert (cert->priv->cert); + + if (cert->priv->asn1) + return g_object_ref (cert->priv->asn1); + + return NULL; +} + +gboolean +e_cert_mark_for_deletion (ECert *cert) +{ + /* nsNSSShutDownPreventionLock locker; */ + +#if 0 + /* make sure user is logged in to the token */ + nsCOMPtr < nsIInterfaceRequestor> ctx = new PipUIContext (); +#endif + + if (PK11_NeedLogin (cert->priv->cert->slot) + && !PK11_NeedUserInit (cert->priv->cert->slot) + && !PK11_IsInternal (cert->priv->cert->slot)) { + if (PK11_Authenticate ( + cert->priv->cert->slot, PR_TRUE, NULL) != SECSuccess) { + return FALSE; + } + } + + cert->priv->delete = TRUE; + + return TRUE; +} + +ECertType +e_cert_get_cert_type (ECert *ecert) +{ + const gchar *nick = e_cert_get_nickname (ecert); + const gchar *email = e_cert_get_email (ecert); + CERTCertificate *cert = ecert->priv->cert; + + if (nick) { + if (e_cert_trust_has_any_user (cert->trust)) + return E_CERT_USER; + if (e_cert_trust_has_any_ca (cert->trust) + || CERT_IsCACert (cert,NULL)) + return E_CERT_CA; + if (e_cert_trust_has_peer (cert->trust, PR_TRUE, PR_FALSE, PR_FALSE)) + return E_CERT_SITE; + } + if (email && e_cert_trust_has_peer (cert->trust, PR_FALSE, PR_TRUE, PR_FALSE)) + return E_CERT_CONTACT; + + return E_CERT_UNKNOWN; +} diff --git a/smime/lib/e-cert.h b/smime/lib/e-cert.h new file mode 100644 index 0000000000..0096336741 --- /dev/null +++ b/smime/lib/e-cert.h @@ -0,0 +1,106 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifndef _E_CERT_H_ +#define _E_CERT_H_ + +#include <glib-object.h> +#include <cert.h> +#include "e-asn1-object.h" + +#define E_TYPE_CERT (e_cert_get_type ()) +#define E_CERT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CERT, ECert)) +#define E_CERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CERT, ECertClass)) +#define E_IS_CERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CERT)) +#define E_IS_CERT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CERT)) +#define E_CERT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_CERT, ECertClass)) + +typedef struct _ECert ECert; +typedef struct _ECertClass ECertClass; +typedef struct _ECertPrivate ECertPrivate; + +typedef enum { + E_CERT_CA, + E_CERT_CONTACT, + E_CERT_SITE, + E_CERT_USER, + E_CERT_UNKNOWN +} ECertType; + +struct _ECert { + GObject parent; + + ECertPrivate *priv; +}; + +struct _ECertClass { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_ecert_reserved0) (void); + void (*_ecert_reserved1) (void); + void (*_ecert_reserved2) (void); + void (*_ecert_reserved3) (void); + void (*_ecert_reserved4) (void); +}; + +GType e_cert_get_type (void); + +ECert * e_cert_new (CERTCertificate *cert); +ECert * e_cert_new_from_der (gchar *data, guint32 len); + +CERTCertificate * e_cert_get_internal_cert (ECert *cert); + +gboolean e_cert_get_raw_der (ECert *cert, gchar **data, guint32 *len); +const gchar * e_cert_get_window_title (ECert *cert); +const gchar * e_cert_get_nickname (ECert *cert); +const gchar * e_cert_get_email (ECert *cert); +const gchar * e_cert_get_org (ECert *cert); +const gchar * e_cert_get_org_unit (ECert *cert); +const gchar * e_cert_get_cn (ECert *cert); +const gchar * e_cert_get_subject_name (ECert *cert); + +const gchar * e_cert_get_issuer_name (ECert *cert); +const gchar * e_cert_get_issuer_cn (ECert *cert); +const gchar * e_cert_get_issuer_org (ECert *cert); +const gchar * e_cert_get_issuer_org_unit (ECert *cert); + +PRTime e_cert_get_issued_on_time (ECert *cert); +const gchar * e_cert_get_issued_on (ECert *cert); +PRTime e_cert_get_expires_on_time (ECert *cert); +const gchar * e_cert_get_expires_on (ECert *cert); +const gchar * e_cert_get_usage (ECert *cert); + +const gchar * e_cert_get_serial_number (ECert *cert); +const gchar * e_cert_get_sha1_fingerprint (ECert *cert); +const gchar * e_cert_get_md5_fingerprint (ECert *cert); + +GList * e_cert_get_issuers_chain (ECert *cert); +ECert * e_cert_get_ca_cert (ECert *ecert); +EASN1Object * e_cert_get_asn1_struct (ECert *cert); + +gboolean e_cert_mark_for_deletion (ECert *cert); + +ECertType e_cert_get_cert_type (ECert *cert); + +#endif /* _E_CERT_H_ */ diff --git a/smime/lib/e-pkcs12.c b/smime/lib/e-pkcs12.c new file mode 100644 index 0000000000..3cf2cb6479 --- /dev/null +++ b/smime/lib/e-pkcs12.c @@ -0,0 +1,371 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* The following is the mozilla license blurb, as the bodies some of + * these functions were derived from the mozilla source. */ +/* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + */ + +/* + * Author: Chris Toshok (toshok@ximian.com) + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include <time.h> +#include <fcntl.h> +#include <unistd.h> + +#include "e-util/e-util.h" + +#include "e-cert-db.h" +#include "e-pkcs12.h" + +#include "prmem.h" +#include "nss.h" +#include "ssl.h" +#include "pkcs12.h" +#include "p12plcy.h" +#include "pk11func.h" +#include "secerr.h" + +/* static callback functions for the NSS PKCS#12 library */ +static SECItem * PR_CALLBACK nickname_collision (SECItem *, PRBool *, gpointer); + +static gboolean handle_error (gint myerr); + +#define PKCS12_BUFFER_SIZE 2048 +#define PKCS12_RESTORE_OK 1 +#define PKCS12_BACKUP_OK 2 +#define PKCS12_USER_CANCELED 3 +#define PKCS12_NOSMARTCARD_EXPORT 4 +#define PKCS12_RESTORE_FAILED 5 +#define PKCS12_BACKUP_FAILED 6 +#define PKCS12_NSS_ERROR 7 + +G_DEFINE_TYPE (EPKCS12, e_pkcs12, G_TYPE_OBJECT) + +static void +e_pkcs12_class_init (EPKCS12Class *class) +{ +} + +static void +e_pkcs12_init (EPKCS12 *ec) +{ +} + +EPKCS12 * +e_pkcs12_new (void) +{ + return g_object_new (E_TYPE_PKCS12, NULL); +} + +static gboolean +input_to_decoder (SEC_PKCS12DecoderContext *dcx, + const gchar *path, + GError **error) +{ + /* nsNSSShutDownPreventionLock locker; */ + SECStatus srv; + gint amount; + gchar buf[PKCS12_BUFFER_SIZE]; + FILE *fp; + + /* open path */ + fp = g_fopen (path, "rb"); + if (!fp) { + /* XXX gerror */ + printf ("couldn't open '%s'\n", path); + return FALSE; + } + + while (TRUE) { + amount = fread (buf, 1, sizeof (buf), fp); + if (amount < 0) { + fclose (fp); + return FALSE; + } + + /* feed the file data into the decoder */ + srv = SEC_PKCS12DecoderUpdate ( + dcx, (guchar *) buf, amount); + if (srv) { + /* XXX g_error */ + fclose (fp); + return FALSE; + } + if (amount < PKCS12_BUFFER_SIZE) + break; + } + fclose (fp); + return TRUE; +} + +/* XXX toshok - this needs to be done using a signal as in the + * e_cert_db_login_to_slot stuff, instead of a direct gui dep here.. + * for now, though, it stays. */ +static gboolean +prompt_for_password (gchar *title, + gchar *prompt, + SECItem *pwd) +{ + gchar *passwd; + + passwd = e_passwords_ask_password ( + title, "", prompt, + E_PASSWORDS_REMEMBER_NEVER | E_PASSWORDS_SECRET, + NULL, NULL); + + if (passwd) { + gsize len = strlen (passwd); + const gchar *inptr = passwd; + guchar *outptr; + gunichar2 c; + + SECITEM_AllocItem (NULL, pwd, sizeof (gunichar2) * (len + 1)); + + outptr = pwd->data; + while (inptr && (c = (gunichar2) (g_utf8_get_char (inptr) & 0xffff))) { + inptr = g_utf8_next_char (inptr); + c = GUINT16_TO_BE (c); + *outptr++ = ((gchar *) &c)[0]; + *outptr++ = ((gchar *) &c)[1]; + } + + outptr[0] = 0; + outptr[1] = 0; + + memset (passwd, 0, strlen (passwd)); + g_free (passwd); + } + + return TRUE; +} + +static gboolean +import_from_file_helper (EPKCS12 *pkcs12, + PK11SlotInfo *slot, + const gchar *path, + gboolean *aWantRetry, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker; */ + gboolean rv; + SECStatus srv = SECSuccess; + SEC_PKCS12DecoderContext *dcx = NULL; + SECItem passwd; + GError *err = NULL; + + *aWantRetry = FALSE; + + passwd.data = NULL; + rv = prompt_for_password ( + _("PKCS12 File Password"), + _("Enter password for PKCS12 file:"), &passwd); + if (!rv) goto finish; + if (passwd.data == NULL) { + handle_error (PKCS12_USER_CANCELED); + return TRUE; + } + + /* initialize the decoder */ + dcx = SEC_PKCS12DecoderStart ( + &passwd, + slot, + /* we specify NULL for all the + * funcs + data so it'll use the + * default pk11wrap functions */ + NULL, NULL, NULL, + NULL, NULL, NULL); + if (!dcx) { + srv = SECFailure; + goto finish; + } + /* read input file and feed it to the decoder */ + rv = input_to_decoder (dcx, path, &err); + if (!rv) { +#ifdef notyet + /* XXX we need this to check the gerror */ + if (NS_ERROR_ABORT == rv) { + /* inputToDecoder indicated a NSS error */ + srv = SECFailure; + } +#else + srv = SECFailure; +#endif + goto finish; + } + + /* verify the blob */ + srv = SEC_PKCS12DecoderVerify (dcx); + if (srv) goto finish; + /* validate bags */ + srv = SEC_PKCS12DecoderValidateBags (dcx, nickname_collision); + if (srv) goto finish; + /* import cert and key */ + srv = SEC_PKCS12DecoderImportBags (dcx); + if (srv) goto finish; + /* Later - check to see if this should become default email cert */ + handle_error (PKCS12_RESTORE_OK); + finish: + /* If srv != SECSuccess, NSS probably set a specific error code. + * We should use that error code instead of inventing a new one + * for every error possible. */ + if (srv != SECSuccess) { + if (SEC_ERROR_BAD_PASSWORD == PORT_GetError ()) { + *aWantRetry = TRUE; + } + handle_error (PKCS12_NSS_ERROR); + } else if (!rv) { + handle_error (PKCS12_RESTORE_FAILED); + } + /* finish the decoder */ + if (dcx) + SEC_PKCS12DecoderFinish (dcx); + return TRUE; +} + +gboolean +e_pkcs12_import_from_file (EPKCS12 *pkcs12, + const gchar *path, + GError **error) +{ + /*nsNSSShutDownPreventionLock locker;*/ + gboolean rv = TRUE; + gboolean wantRetry; + PK11SlotInfo *slot; + + printf ("importing pkcs12 from '%s'\n", path); + + slot = PK11_GetInternalKeySlot (); + + if (!e_cert_db_login_to_slot (e_cert_db_peek (), slot)) + return FALSE; + + do { + rv = import_from_file_helper (pkcs12, slot, path, &wantRetry, error); + } while (rv && wantRetry); + + return rv; +} + +gboolean +e_pkcs12_export_to_file (EPKCS12 *pkcs12, + const gchar *path, + GList *certs, + GError **error) +{ + return FALSE; +} + +/* what to do when the nickname collides with one already in the db. + * TODO: not handled, throw a dialog allowing the nick to be changed? */ +static SECItem * PR_CALLBACK +nickname_collision (SECItem *oldNick, + PRBool *cancel, + gpointer wincx) +{ + /* nsNSSShutDownPreventionLock locker; */ + gint count = 1; + gchar *nickname = NULL; + gchar *default_nickname = _("Imported Certificate"); + SECItem *new_nick; + + *cancel = PR_FALSE; + printf ("nickname_collision\n"); + + /* The user is trying to import a PKCS#12 file that doesn't have the + * attribute we use to set the nickname. So in order to reduce the + * number of interactions we require with the user, we'll build a nickname + * for the user. The nickname isn't prominently displayed in the UI, + * so it's OK if we generate one on our own here. + * XXX If the NSS API were smarter and actually passed a pointer to + * the CERTCertificate * we're importing we could actually just + * call default_nickname (which is what the issuance code path + * does) and come up with a reasonable nickname. Alas, the NSS + * API limits our ability to produce a useful nickname without + * bugging the user. :( + */ + while (1) { + CERTCertificate *cert; + + /* If we've gotten this far, that means there isn't a certificate + * in the database that has the same subject name as the cert we're + * trying to import. So we need to come up with a "nickname" to + * satisfy the NSS requirement or fail in trying to import. + * Basically we use a default nickname from a properties file and + * see if a certificate exists with that nickname. If there isn't, then + * create update the count by one and append the string '#1' Or + * whatever the count currently is, and look for a cert with + * that nickname. Keep updating the count until we find a nickname + * without a corresponding cert. + * XXX If a user imports *many * certs without the 'friendly name' + * attribute, then this may take a long time. :( + */ + if (count > 1) { + g_free (nickname); + nickname = g_strdup_printf ("%s #%d", default_nickname, count); + } else { + g_free (nickname); + nickname = g_strdup (default_nickname); + } + cert = CERT_FindCertByNickname ( + CERT_GetDefaultCertDB (), + nickname); + if (!cert) { + break; + } + CERT_DestroyCertificate (cert); + count++; + } + + new_nick = PR_Malloc (sizeof (SECItem)); + new_nick->type = siAsciiString; + new_nick->data = (guchar *) nickname; + new_nick->len = strlen ((gchar *) new_nick->data); + return new_nick; +} + +static gboolean +handle_error (gint myerr) +{ + printf ("handle_error (%d)\n", myerr); + + return FALSE; +} diff --git a/smime/lib/e-pkcs12.h b/smime/lib/e-pkcs12.h new file mode 100644 index 0000000000..940187bc18 --- /dev/null +++ b/smime/lib/e-pkcs12.h @@ -0,0 +1,68 @@ +/* + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifndef _E_PKCS12_H_ +#define _E_PKCS12_H_ + +#include <glib-object.h> + +#define E_TYPE_PKCS12 (e_pkcs12_get_type ()) +#define E_PKCS12(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_PKCS12, EPKCS12)) +#define E_PKCS12_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_PKCS12, EPKCS12Class)) +#define E_IS_PKCS12(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_PKCS12)) +#define E_IS_PKCS12_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_PKCS12)) +#define E_PKCS12_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_PKCS12, EPKCS12Class)) + +typedef struct _EPKCS12 EPKCS12; +typedef struct _EPKCS12Class EPKCS12Class; +typedef struct _EPKCS12Private EPKCS12Private; + +struct _EPKCS12 { + GObject parent; + + EPKCS12Private *priv; +}; + +struct _EPKCS12Class { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_epkcs12_reserved0) (void); + void (*_epkcs12_reserved1) (void); + void (*_epkcs12_reserved2) (void); + void (*_epkcs12_reserved3) (void); + void (*_epkcs12_reserved4) (void); +}; + +GType e_pkcs12_get_type (void); + +EPKCS12 * e_pkcs12_new (void); + +#if 0 +/* XXX we're not going to support additional slots in the initial ssl + * stuff, so we just always default to the internal token (and thus + * don't need this function yet. */ +gboolean e_pkcs12_set_token (void); +#endif + +gboolean e_pkcs12_import_from_file (EPKCS12 *pkcs12, const gchar *path, GError **error); +gboolean e_pkcs12_export_to_file (EPKCS12 *pkcs12, const gchar *path, GList *certs, GError **error); + +#endif /* _E_CERT_H_ */ diff --git a/smime/tests/Makefile.am b/smime/tests/Makefile.am new file mode 100644 index 0000000000..211042d9e1 --- /dev/null +++ b/smime/tests/Makefile.am @@ -0,0 +1,20 @@ +noinst_PROGRAMS=import-cert + +TEST_CPPFLAGS= \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir)/smime/lib \ + $(EVOLUTION_DATA_SERVER_CFLAGS) \ + $(GNOME_PLATFORM_CFLAGS) \ + $(CERT_UI_CFLAGS) + +TEST_LIBS= \ + $(top_builddir)/smime/lib/libessmime.la \ + -L/home/toshok/src/mozilla/mozilla/dist/lib \ + $(CERT_UI_LIBS) \ + $(top_builddir)/e-util/libevolution-util.la \ + $(EVOLUTION_DATA_SERVER_LIBS) \ + $(GNOME_PLATFORM_LIBS) + +import_cert_LDADD=$(TEST_LIBS) + +-include $(top_srcdir)/git.mk diff --git a/smime/tests/import-cert.c b/smime/tests/import-cert.c new file mode 100644 index 0000000000..dfd368da46 --- /dev/null +++ b/smime/tests/import-cert.c @@ -0,0 +1,57 @@ +/* + * + * 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 of the License, or (at your option) version 3. + * + * 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include <gtk/gtk.h> + +#include "e-cert-db.h" +#include "e-pkcs12.h" + +gint +main (gint argc, + gchar **argv) +{ + ECertDB *db; + EPKCS12 *pkcs12; + + gtk_init (&argc, &argv); + + db = e_cert_db_peek (); + + if (!e_cert_db_import_certs_from_file (db, "ca.crt", E_CERT_CA, NULL /* XXX */)) { + g_warning ("CA cert import failed"); + } + + if (!e_cert_db_import_certs_from_file (db, "", E_CERT_CONTACT, NULL /* XXX */)) { + g_warning ("contact cert import failed"); + } + + if (!e_cert_db_import_certs_from_file (db, "", E_CERT_SITE, NULL /* XXX */)) { + g_warning ("server cert import failed"); + } + + pkcs12 = e_pkcs12_new (); + if (!e_pkcs12_import_from_file (pkcs12, "newcert.p12", NULL /* XXX */)) { + g_warning ("PKCS12 import failed"); + } + + e_cert_db_shutdown (); + + return 0; +} |