From: Milan Crha Date: Tue, 11 Dec 2012 17:38:49 +0000 (+0100) Subject: [trust-prompt] Add certificate viewer and "issuer*" parameters X-Git-Tag: upstream/3.7.4~131 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3ba0319ee232d0be1508d18f9f5a132dd0b8c683;p=platform%2Fupstream%2Fevolution-data-server.git [trust-prompt] Add certificate viewer and "issuer*" parameters --- diff --git a/modules/trust-prompt/Makefile.am b/modules/trust-prompt/Makefile.am index 53b193a..859cdd9 100644 --- a/modules/trust-prompt/Makefile.am +++ b/modules/trust-prompt/Makefile.am @@ -13,6 +13,10 @@ module_trust_prompt_la_CPPFLAGS = \ $(NULL) module_trust_prompt_la_SOURCES = \ + e-asn1-object.c \ + e-asn1-object.h \ + certificate-viewer.c \ + certificate-viewer.h \ module-trust-prompt.c \ $(NULL) diff --git a/modules/trust-prompt/certificate-viewer.c b/modules/trust-prompt/certificate-viewer.c new file mode 100644 index 0000000..ce96e50 --- /dev/null +++ b/modules/trust-prompt/certificate-viewer.c @@ -0,0 +1,678 @@ +/* + * 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 + * + * + * Authors: + * Chris Toshok + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "pk11pub.h" +#include "hasht.h" + +#include + +#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 ("<", _("Not part of certificate"), ">", 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 ("", port_str, "", 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 ("", port_str, "", 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/modules/trust-prompt/certificate-viewer.h b/modules/trust-prompt/certificate-viewer.h new file mode 100644 index 0000000..41f2c83 --- /dev/null +++ b/modules/trust-prompt/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 + * + * + * Authors: + * Chris Toshok + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifndef CERTIFICATE_VIEWER_H +#define CERTIFICATE_VIEWER_H + +#include +#include + +GtkWidget * certificate_viewer_new (GtkWindow *parent, + const CERTCertificate *cert, + const GSList *issuers_chain_certs); + +#endif /* CERTIFICATE_VIEWER_H */ diff --git a/modules/trust-prompt/e-asn1-object.c b/modules/trust-prompt/e-asn1-object.c new file mode 100644 index 0000000..7e5f27c --- /dev/null +++ b/modules/trust-prompt/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 +#endif + +#include + +#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/modules/trust-prompt/e-asn1-object.h b/modules/trust-prompt/e-asn1-object.h new file mode 100644 index 0000000..39b79a7 --- /dev/null +++ b/modules/trust-prompt/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 + * + * + * Authors: + * Chris Toshok + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + */ + +#ifndef E_ASN1_OBJECT_H +#define E_ASN1_OBJECT_H + +#include + +#include + +#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/modules/trust-prompt/module-trust-prompt.c b/modules/trust-prompt/module-trust-prompt.c index a818424..567bd41 100644 --- a/modules/trust-prompt/module-trust-prompt.c +++ b/modules/trust-prompt/module-trust-prompt.c @@ -27,6 +27,8 @@ #include +#include "certificate-viewer.h" + /* Standard GObject macros */ #define E_TYPE_TRUST_PROMPT (e_trust_prompt_get_type ()) #define E_TRUST_PROMPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_TRUST_PROMPT, ETrustPrompt)) @@ -142,6 +144,12 @@ e_module_unload (GTypeModule *type_module) "certificate" - a base64-encoded DER certificate, for which ask on trust "certificate-errors" - a hexa-decimal integer (as string) corresponding to GTlsCertificateFlags + It can contain, optionally, chain of issuers: + "issuer" - a base64-encoded DER certificate, issuer of "certificate" + "issuer-1" - a base64-encoded DER certificate, issuer of "issuer" + "issuer-2" - a base64-encoded DER certificate, issuer of "issuer-1" + and so on + Result of the dialog is: 0 - reject 1 - accept permanently @@ -264,7 +272,27 @@ trust_prompt_add_info_line (GtkGrid *grid, pango_attr_list_unref (bold); } -#define TRUST_PROMP_ID_KEY "ETrustPrompt::prompt-id-key" +#define TRUST_PROMP_ID_KEY "ETrustPrompt::prompt-id-key" +#define TRUST_PROMP_CERT_KEY "ETrustPrompt::cert-key" +#define TRUST_PROMP_ISSUERS_KEY "ETrustPrompt::issuers-key" + +static void +trust_prompt_free_certificate (gpointer cert) +{ + if (!cert) + return; + + CERT_DestroyCertificate (cert); +} + +static void +trust_prompt_free_issuers (gpointer issuers) +{ + if (!issuers) + return; + + g_slist_free_full (issuers, trust_prompt_free_certificate); +} static void trust_prompt_response_cb (GtkWidget *dialog, @@ -274,7 +302,15 @@ trust_prompt_response_cb (GtkWidget *dialog, gint prompt_id; if (response == GTK_RESPONSE_HELP) { - /* view certificate */ + GtkWidget *viewer; + + viewer = certificate_viewer_new (GTK_WINDOW (dialog), + g_object_get_data (G_OBJECT (dialog), TRUST_PROMP_CERT_KEY), + g_object_get_data (G_OBJECT (dialog), TRUST_PROMP_ISSUERS_KEY)); + + gtk_dialog_run (GTK_DIALOG (viewer)); + gtk_widget_destroy (viewer); + return; } @@ -293,6 +329,54 @@ trust_prompt_response_cb (GtkWidget *dialog, e_user_prompter_server_extension_response (extension, prompt_id, response, NULL); } +static GSList * +trust_prompt_get_issuers (CERTCertDBHandle *certdb, + const ENamedParameters *parameters) +{ + GSList *issuers = NULL; + CERTCertificate *cert; + SECItem derCert; + gsize derCert_len = 0; + gint ii; + + g_return_val_if_fail (certdb != NULL, NULL); + g_return_val_if_fail (parameters != NULL, NULL); + + for (ii = 0; ii >= 0; ii++) { + const gchar *base64_cert; + + if (ii == 0) { + base64_cert = e_named_parameters_get (parameters, "issuer"); + } else { + gchar *key; + + key = g_strdup_printf ("issuer-%d", ii); + base64_cert = e_named_parameters_get (parameters, key); + g_free (key); + } + + if (!base64_cert) + break; + + derCert.type = siDERCertBuffer; + derCert.data = g_base64_decode (base64_cert, &derCert_len); + if (!derCert.data) + break; + + derCert.len = derCert_len; + + cert = CERT_NewTempCertificate (certdb, &derCert, NULL, PR_FALSE, PR_TRUE); + g_free (derCert.data); + + if (!cert) + break; + + issuers = g_slist_prepend (issuers, cert); + } + + return g_slist_reverse (issuers); +} + static gboolean trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension, gint prompt_id, @@ -306,6 +390,7 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension, GtkGrid *grid; CERTCertDBHandle *certdb; CERTCertificate *cert; + GSList *issuers; SECItem derCert; gsize derCert_len = 0; @@ -329,6 +414,8 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension, cert = CERT_NewTempCertificate (certdb, &derCert, NULL, PR_FALSE, PR_TRUE); g_return_val_if_fail (cert != NULL, FALSE); + issuers = trust_prompt_get_issuers (certdb, parameters); + cert_errs = g_ascii_strtoll (cert_errs_str, NULL, 16); dialog = gtk_dialog_new_with_buttons (_("Certificate trust..."), NULL, 0, @@ -339,7 +426,6 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension, NULL); gtk_window_set_icon_name (GTK_WINDOW (dialog), "evolution"); - gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_HELP, FALSE); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); grid = g_object_new (GTK_TYPE_GRID, @@ -394,12 +480,14 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension, g_free (tmp); g_object_set_data (G_OBJECT (dialog), TRUST_PROMP_ID_KEY, GINT_TO_POINTER (prompt_id)); + g_object_set_data_full (G_OBJECT (dialog), TRUST_PROMP_CERT_KEY, cert, trust_prompt_free_certificate); + g_object_set_data_full (G_OBJECT (dialog), TRUST_PROMP_ISSUERS_KEY, issuers, trust_prompt_free_issuers); + g_signal_connect (dialog, "response", G_CALLBACK (trust_prompt_response_cb), extension); gtk_widget_show_all (GTK_WIDGET (grid)); gtk_widget_show (dialog); - CERT_DestroyCertificate (cert); g_free (derCert.data); return TRUE; diff --git a/po/POTFILES.in b/po/POTFILES.in index a66d380..cb46eb9 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -230,6 +230,8 @@ libedataserverui/e-passwords-win32.c libedataserverui/e-source-selector-dialog.c modules/gnome-online-accounts/goaewsclient.c modules/google-backend/module-google-backend.c +modules/trust-prompt/certificate-viewer.c +modules/trust-prompt/e-asn1-object.c modules/trust-prompt/module-trust-prompt.c modules/yahoo-backend/module-yahoo-backend.c services/evolution-addressbook-factory/evolution-addressbook-factory.c diff --git a/tests/libedataserver/e-user-prompter-test.c b/tests/libedataserver/e-user-prompter-test.c index 8ad04cb..aabb2c3 100644 --- a/tests/libedataserver/e-user-prompter-test.c +++ b/tests/libedataserver/e-user-prompter-test.c @@ -22,9 +22,11 @@ typedef struct _TestClosure TestClosure; typedef struct _TestFixture TestFixture; struct _TestClosure { + gboolean only_certificate; }; struct _TestFixture { + gboolean only_certificate; EUserPrompter *prompter; GMainLoop *main_loop; }; @@ -93,6 +95,34 @@ test_trust_prompt (EUserPrompter *prompter) "3zOMCaTr7Cq6SJqnlrYUYbdBkobjadcfG2eAKfbhOiVGVEOee4O6JJ+nCrqXpqj42EGuQ8mKvl7Kao+" "xerxctag0jzlLRFWJ69l7DZZyyFzY+/I9IWSVj8i0VCz0FkulK9adKeYD4E4BAOQvDFY4ED2FckW3AZ" "zVueeiqTSIKwkDFhSDwTJsIfsOaEQ=="; + const gchar *der_certificate_issuer = + "MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3Rh" + "cnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcG" + "A1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcx" + "MDI0MjA1NzA5WjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT" + "IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0YXJ0Q29tIENsYXNz" + "IDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB" + "CgKCAQEA4k85L6GMmoWtCA4IPlfyiAEhG5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l" + "33F0NENVsTUJm9m8H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL" + "hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGEd2H2wnztwI14FBiZ" + "gZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa8zVhwQIpkFR+ZE3EMFICgtffziFuGJHX" + "uKuMJxe18KMBL47SLoc6PbQpZ4rEAwIDAQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B" + "Af8EBAMCAQYwHQYDVR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul" + "F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDovL29jc3Auc3RhcnRz" + "c2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYD" + "VR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRw" + "Oi8vY3JsLnN0YXJ0c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu" + "BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0BggrBgEFBQcCARYo" + "aHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRlLnBkZjANBgkqhkiG9w0BAQUFAAOCAgEA" + "nQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0UL" + "pLA4Gmj0lRPM4EOU7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox" + "uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntNrCEnME5ZRmqTF8rI" + "OS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hIt" + "uO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6SiBeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjH" + "eOBiS0N9PN7WmrQQoUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P" + "4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y6NjAbx/rJnWfk/x7" + "G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd60Y+NWZP8P3PXLrQsldiL98l+x/ydrHIE" + "H9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3vqhykguAzx/Q="; ENamedParameters *parameters; GError *error = NULL; gint result; @@ -101,9 +131,10 @@ test_trust_prompt (EUserPrompter *prompter) parameters = e_named_parameters_new (); - e_named_parameters_set (parameters, "host", "https://bugzilla.gnome.org/"); + e_named_parameters_set (parameters, "host", "bugzilla.gnome.org"); e_named_parameters_set (parameters, "certificate", der_certificate); e_named_parameters_set (parameters, "certificate-errors", "007f"); + e_named_parameters_set (parameters, "issuer", der_certificate_issuer); result = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters, NULL, NULL, &error); @@ -186,7 +217,7 @@ test_user_prompts_idle_cb (gpointer user_data) in the right order and only one at a time, and then run the last synchronously, to wait for the result */ sz = G_N_ELEMENTS (prompts); - for (ii = 0; ii < sz && !error; ii++) { + for (ii = 0; !fixture->only_certificate && ii < sz && !error; ii++) { gchar *title, *primary, *secondary, **buttons = NULL; GSList *captions = NULL; @@ -244,6 +275,10 @@ static void test_user_prompts (TestFixture *fixture, gconstpointer user_data) { + const TestClosure *closure = user_data; + + fixture->only_certificate = closure->only_certificate; + g_idle_add (test_user_prompts_idle_cb, fixture); g_main_loop_run (fixture->main_loop); } @@ -260,6 +295,8 @@ main (gint argc, g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/"); + closure.only_certificate = argc > 1 && g_ascii_strcasecmp (argv[1], "cert-only") == 0; + g_test_add ( "/e-user-prompter-test/UserPrompts", TestFixture, &closure,