2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
17 * Chris Toshok <toshok@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
26 #include <glib/gi18n.h>
31 #include <libedataserver/libedataserver.h>
33 #include "e-asn1-object.h"
34 #include "certificate-viewer.h"
36 #define CERTIFICATE_VIEWER_PRIV_KEY "CertificateViewerPriv-key"
38 typedef struct _CertificateViewerPriv
40 GtkWidget *issued_to_cn;
41 GtkWidget *issued_to_o;
42 GtkWidget *issued_to_ou;
43 GtkWidget *issued_to_serial;
44 GtkWidget *issued_by_cn;
45 GtkWidget *issued_by_o;
46 GtkWidget *issued_by_ou;
47 GtkWidget *validity_issued_on;
48 GtkWidget *validity_expires_on;
49 GtkWidget *fingerprints_sha1;
50 GtkWidget *fingerprints_md5;
51 GtkWidget *cert_hierarchy_treeview;
52 GtkWidget *cert_fields_treeview;
53 GtkWidget *cert_field_value_textview;
55 CERTCertificate *cert;
57 GtkTextTag *monospace_tag;
58 } CertificateViewerPriv;
61 free_priv_struct (gpointer ptr)
63 CertificateViewerPriv *priv = ptr;
70 CERT_DestroyCertificate (priv->cert);
72 for (iter = priv->issuers; iter; iter = iter->next) {
73 CERTCertificate *cert = iter->data;
76 CERT_DestroyCertificate (cert);
79 g_slist_free (priv->issuers);
85 begin_section (GtkGrid *add_to,
94 g_return_if_fail (add_to != NULL);
95 g_return_if_fail (caption != NULL);
96 g_return_if_fail (from_row != NULL);
98 bold = pango_attr_list_new ();
99 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
100 pango_attr_list_insert (bold, attr);
102 widget = gtk_label_new (caption);
103 g_object_set (G_OBJECT (widget),
105 "halign", GTK_ALIGN_START,
106 "justify", GTK_JUSTIFY_LEFT,
108 "ellipsize", PANGO_ELLIPSIZE_NONE,
111 pango_attr_list_unref (bold);
113 gtk_grid_attach (add_to, widget, 0, *from_row, 3, 1);
116 widget = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
117 gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 12, 0);
119 gtk_grid_attach (add_to, widget, 0, *from_row, 1, for_rows);
123 add_info_label (GtkGrid *add_to,
124 const gchar *caption,
129 g_return_val_if_fail (add_to != NULL, NULL);
130 g_return_val_if_fail (at_row != NULL, NULL);
133 widget = gtk_label_new (caption);
134 g_object_set (G_OBJECT (widget),
136 "halign", GTK_ALIGN_START,
137 "justify", GTK_JUSTIFY_LEFT,
138 "ellipsize", PANGO_ELLIPSIZE_NONE,
141 gtk_grid_attach (add_to, widget, 1, *at_row, 1, 1);
144 widget = gtk_label_new ("");
145 g_object_set (G_OBJECT (widget),
147 "halign", GTK_ALIGN_START,
148 "justify", GTK_JUSTIFY_LEFT,
149 "ellipsize", PANGO_ELLIPSIZE_NONE,
150 "selectable", caption != NULL,
153 gtk_grid_attach (add_to, widget, caption ? 2 : 1, *at_row, caption ? 1 : 2, 1);
161 add_scrolled_window (GtkGrid *add_to,
162 const gchar *caption,
164 GtkWidget *add_widget)
167 PangoAttribute *attr;
170 g_return_val_if_fail (add_to != NULL, NULL);
171 g_return_val_if_fail (caption != NULL, NULL);
172 g_return_val_if_fail (at_row != NULL, NULL);
174 bold = pango_attr_list_new ();
175 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
176 pango_attr_list_insert (bold, attr);
178 widget = gtk_label_new (caption);
179 g_object_set (G_OBJECT (widget),
181 "halign", GTK_ALIGN_START,
182 "justify", GTK_JUSTIFY_LEFT,
184 "ellipsize", PANGO_ELLIPSIZE_NONE,
187 pango_attr_list_unref (bold);
189 gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
192 widget = gtk_scrolled_window_new (NULL, NULL);
193 g_object_set (G_OBJECT (widget),
195 "halign", GTK_ALIGN_FILL,
197 "valign", GTK_ALIGN_FILL,
198 "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
199 "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
200 "shadow-type", GTK_SHADOW_ETCHED_IN,
203 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (widget), add_widget);
205 gtk_grid_attach (add_to, widget, 0, *at_row, 1, 1);
211 #define FLAG_NONE (0)
212 #define FLAG_PORT_MEMORY (1 << 0)
213 #define FLAG_MARKUP (1 << 1)
216 set_label_text (GtkWidget *label,
217 const gchar *not_part_markup,
222 if ((flags & FLAG_MARKUP) != 0)
223 gtk_label_set_markup (GTK_LABEL (label), text);
225 gtk_label_set_text (GTK_LABEL (label), text);
227 if ((flags & FLAG_PORT_MEMORY) != 0)
232 gtk_label_set_markup (GTK_LABEL (label), not_part_markup);
237 get_cert_times (CERTCertificate *cert,
241 PRTime time_issued_on;
242 PRTime time_expires_on;
243 PRExplodedTime explodedTime;
244 struct tm exploded_tm;
247 g_return_if_fail (cert != NULL);
248 g_return_if_fail (issued_on != NULL);
249 g_return_if_fail (expires_on != NULL);
251 if (SECSuccess != CERT_GetCertTimes (cert, &time_issued_on, &time_expires_on))
254 PR_ExplodeTime (time_issued_on, PR_LocalTimeParameters, &explodedTime);
255 exploded_tm.tm_sec = explodedTime.tm_sec;
256 exploded_tm.tm_min = explodedTime.tm_min;
257 exploded_tm.tm_hour = explodedTime.tm_hour;
258 exploded_tm.tm_mday = explodedTime.tm_mday;
259 exploded_tm.tm_mon = explodedTime.tm_month;
260 exploded_tm.tm_year = explodedTime.tm_year - 1900;
261 e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
262 *issued_on = g_strdup (buf);
264 PR_ExplodeTime (time_expires_on, PR_LocalTimeParameters, &explodedTime);
265 exploded_tm.tm_sec = explodedTime.tm_sec;
266 exploded_tm.tm_min = explodedTime.tm_min;
267 exploded_tm.tm_hour = explodedTime.tm_hour;
268 exploded_tm.tm_mday = explodedTime.tm_mday;
269 exploded_tm.tm_mon = explodedTime.tm_month;
270 exploded_tm.tm_year = explodedTime.tm_year - 1900;
271 e_utf8_strftime (buf, sizeof (buf), "%x", &exploded_tm);
272 *expires_on = g_strdup (buf);
276 fill_general_page (CertificateViewerPriv *priv)
278 gchar *not_part_markup;
279 gchar *issued_on = NULL;
280 gchar *expires_on = NULL;
282 guchar fingerprint[128];
285 g_return_if_fail (priv != NULL);
287 not_part_markup = g_strconcat ("<i><", _("Not part of certificate"), "></i>", NULL);
289 set_label_text (priv->issued_to_cn, not_part_markup, CERT_GetCommonName (&priv->cert->subject), FLAG_PORT_MEMORY);
290 set_label_text (priv->issued_to_o, not_part_markup, CERT_GetOrgName (&priv->cert->subject), FLAG_PORT_MEMORY);
291 set_label_text (priv->issued_to_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->subject), FLAG_PORT_MEMORY);
292 set_label_text (priv->issued_to_serial, not_part_markup, CERT_Hexify (&priv->cert->serialNumber, TRUE), FLAG_PORT_MEMORY);
294 set_label_text (priv->issued_by_cn, not_part_markup, CERT_GetCommonName (&priv->cert->issuer), FLAG_PORT_MEMORY);
295 set_label_text (priv->issued_by_o, not_part_markup, CERT_GetOrgName (&priv->cert->issuer), FLAG_PORT_MEMORY);
296 set_label_text (priv->issued_by_ou, not_part_markup, CERT_GetOrgUnitName (&priv->cert->issuer), FLAG_PORT_MEMORY);
298 get_cert_times (priv->cert, &issued_on, &expires_on);
299 set_label_text (priv->validity_issued_on, not_part_markup, issued_on, FLAG_NONE);
300 set_label_text (priv->validity_expires_on, not_part_markup, expires_on, FLAG_NONE);
302 memset (fingerprint, 0, sizeof fingerprint);
304 SEC_OID_SHA1, fingerprint,
305 priv->cert->derCert.data,
306 priv->cert->derCert.len);
307 fpItem.data = fingerprint;
308 fpItem.len = SHA1_LENGTH;
309 port_str = CERT_Hexify (&fpItem, TRUE);
310 set_label_text (priv->fingerprints_sha1, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
311 PORT_Free (port_str);
313 memset (fingerprint, 0, sizeof fingerprint);
315 SEC_OID_MD5, fingerprint,
316 priv->cert->derCert.data,
317 priv->cert->derCert.len);
318 fpItem.data = fingerprint;
319 fpItem.len = MD5_LENGTH;
320 port_str = CERT_Hexify (&fpItem, TRUE);
321 set_label_text (priv->fingerprints_md5, not_part_markup, g_strconcat ("<tt>", port_str, "</tt>", NULL), FLAG_MARKUP);
322 PORT_Free (port_str);
324 g_free (not_part_markup);
328 populate_fields_tree (CertificateViewerPriv *priv,
332 GtkTreeStore *fields_store;
333 GtkTreeIter new_iter;
338 fields_store = GTK_TREE_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->cert_fields_treeview)));
340 /* first insert a node for the current asn1 */
341 gtk_tree_store_insert (fields_store, &new_iter, root, -1);
343 fields_store, &new_iter,
344 0, e_asn1_object_get_display_name (asn1),
348 if (e_asn1_object_is_valid_container (asn1)) {
349 GList *children = e_asn1_object_get_children (asn1);
353 for (iter = children; iter; iter = iter->next) {
354 EASN1Object *subasn1 = iter->data;
356 populate_fields_tree (priv, subasn1, &new_iter);
360 g_list_free_full (children, g_object_unref);
365 hierarchy_selection_changed_cb (GtkTreeSelection *selection,
366 CertificateViewerPriv *priv)
371 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
372 CERTCertificate *cert;
374 GtkTreeStore *fields_store;
376 gtk_tree_model_get (model, &iter, 1, &cert, -1);
381 /* display the cert's ASN1 structure */
382 asn1 = e_asn1_object_new_from_cert (cert);
384 /* wipe out the old model */
385 fields_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
386 gtk_tree_view_set_model (
387 GTK_TREE_VIEW (priv->cert_fields_treeview),
388 GTK_TREE_MODEL (fields_store));
390 /* populate the fields from the newly selected cert */
391 populate_fields_tree (priv, asn1, NULL);
392 gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_fields_treeview));
394 g_object_unref (asn1);
396 /* and blow away the field value */
397 gtk_text_buffer_set_text (
398 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
404 fields_selection_changed_cb (GtkTreeSelection *selection,
405 CertificateViewerPriv *priv)
410 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
411 EASN1Object *asn1 = NULL;
412 const gchar *value = NULL;
413 GtkTextView *textview;
414 GtkTextBuffer *textbuffer;
416 gtk_tree_model_get (model, &iter, 1, &asn1, -1);
419 value = e_asn1_object_get_display_value (asn1);
421 textview = GTK_TEXT_VIEW (priv->cert_field_value_textview);
422 textbuffer = gtk_text_view_get_buffer (textview);
424 gtk_text_buffer_set_text (textbuffer, "", 0);
427 GtkTextIter text_iter;
429 gtk_text_buffer_get_start_iter (textbuffer, &text_iter);
431 gtk_text_buffer_insert_with_tags (textbuffer, &text_iter,
432 value, strlen (value),
433 priv->monospace_tag, NULL);
437 g_object_unref (asn1);
442 fill_details_page (CertificateViewerPriv *priv)
446 GtkTreeSelection *selection;
447 gboolean root_set = FALSE;
448 GtkTreeStore *hierarchy_store;
450 g_return_if_fail (priv != NULL);
452 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_hierarchy_treeview), FALSE);
454 hierarchy_store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
455 gtk_tree_view_set_model (
456 GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
457 GTK_TREE_MODEL (hierarchy_store));
459 gtk_tree_view_insert_column_with_attributes (
460 GTK_TREE_VIEW (priv->cert_hierarchy_treeview),
461 -1, "Cert", gtk_cell_renderer_text_new (),
464 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
466 selection, "changed",
467 G_CALLBACK (hierarchy_selection_changed_cb), priv);
469 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->cert_fields_treeview), FALSE);
471 gtk_tree_view_insert_column_with_attributes (
472 GTK_TREE_VIEW (priv->cert_fields_treeview),
473 -1, "Field", gtk_cell_renderer_text_new (),
476 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->cert_fields_treeview));
478 selection, "changed",
479 G_CALLBACK (fields_selection_changed_cb), priv);
481 /* set the font of the field value viewer to be some fixed
482 * width font to the hex display looks nice. */
483 priv->monospace_tag = gtk_text_buffer_create_tag (
484 gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->cert_field_value_textview)),
485 "mono", "font", "Mono", NULL);
487 /* initially populate the hierarchy from the issuers' chain */
488 for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
489 CERTCertificate *cert = iter->data;
491 GtkTreeIter new_iter;
496 str = CERT_GetCommonName (&cert->subject);
498 gtk_tree_store_insert (hierarchy_store, &new_iter, root_set ? &root : NULL, -1);
500 hierarchy_store, &new_iter,
501 0, str ? str : cert->subjectName,
512 gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->cert_hierarchy_treeview));
516 get_window_title (CERTCertificate *cert)
520 g_return_val_if_fail (cert != NULL, NULL);
523 return g_strdup (cert->nickname);
525 str = CERT_GetCommonName (&cert->subject);
529 title = g_strdup (str);
535 return cert->subjectName;
539 certificate_viewer_new (GtkWindow *parent,
540 const CERTCertificate *cert,
541 const GSList *issuers_chain_certs)
543 CertificateViewerPriv *priv;
544 GtkWidget *dialog, *notebook, *widget;
550 g_return_val_if_fail (cert != NULL, NULL);
552 priv = g_new0 (CertificateViewerPriv, 1);
553 priv->cert = CERT_DupCertificate ((CERTCertificate *) cert);
554 priv->issuers = g_slist_copy ((GSList *) issuers_chain_certs);
556 /* root issuer first, then bottom down to certificate itself */
557 priv->issuers = g_slist_reverse (priv->issuers);
558 priv->issuers = g_slist_append (priv->issuers, priv->cert);
560 for (iter = priv->issuers; iter; iter = g_slist_next (iter)) {
561 iter->data = CERT_DupCertificate (iter->data);
564 title = get_window_title (priv->cert);
566 dialog = gtk_dialog_new_with_buttons (title, parent,
567 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
568 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
573 g_object_set_data_full (G_OBJECT (dialog), CERTIFICATE_VIEWER_PRIV_KEY, priv, free_priv_struct);
575 notebook = gtk_notebook_new ();
576 gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), notebook);
577 gtk_container_set_border_width (GTK_CONTAINER (notebook), 12);
581 grid = GTK_GRID (gtk_grid_new ());
582 g_object_set (G_OBJECT (grid),
584 "halign", GTK_ALIGN_FILL,
586 "valign", GTK_ALIGN_START,
592 begin_section (grid, _("This certificate has been verified for the following uses:"), &row, 4);
594 if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageSSLClient) != 0) {
595 widget = add_info_label (grid, NULL, &row);
596 gtk_label_set_text (GTK_LABEL (widget), _("SSL Client Certificate"));
599 if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & (certificateUsageSSLServer | certificateUsageSSLCA)) != 0) {
600 widget = add_info_label (grid, NULL, &row);
601 gtk_label_set_text (GTK_LABEL (widget), _("SSL Server Certificate"));
604 if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailSigner) != 0) {
605 widget = add_info_label (grid, NULL, &row);
606 gtk_label_set_text (GTK_LABEL (widget), _("Email Signer Certificate"));
609 if (!priv->cert->keyUsagePresent || (priv->cert->keyUsage & certificateUsageEmailRecipient) != 0) {
610 widget = add_info_label (grid, NULL, &row);
611 gtk_label_set_text (GTK_LABEL (widget), _("Email Recipient Certificate"));
614 widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
615 g_object_set (G_OBJECT (widget),
617 "halign", GTK_ALIGN_FILL,
619 "valign", GTK_ALIGN_START,
622 gtk_grid_attach (grid, widget, 0, row, 3, 1);
625 begin_section (grid, _("Issued To"), &row, 4);
626 priv->issued_to_cn = add_info_label (grid, _("Common Name (CN)"), &row);
627 priv->issued_to_o = add_info_label (grid, _("Organization (O)"), &row);
628 priv->issued_to_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
629 priv->issued_to_serial = add_info_label (grid, _("Serial Number"), &row);
631 begin_section (grid, _("Issued By"), &row, 3);
632 priv->issued_by_cn = add_info_label (grid, _("Common Name (CN)"), &row);
633 priv->issued_by_o = add_info_label (grid, _("Organization (O)"), &row);
634 priv->issued_by_ou = add_info_label (grid, _("Organizational Unit (OU)"), &row);
636 begin_section (grid, _("Validity"), &row, 2);
637 priv->validity_issued_on = add_info_label (grid, _("Issued On"), &row);
638 priv->validity_expires_on = add_info_label (grid, _("Expires On"), &row);
640 begin_section (grid, _("Fingerprints"), &row, 2);
641 priv->fingerprints_sha1 = add_info_label (grid, _("SHA1 Fingerprint"), &row);
642 priv->fingerprints_md5 = add_info_label (grid, _("MD5 Fingerprint"), &row);
644 widget = gtk_label_new (_("General"));
645 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
649 grid = GTK_GRID (gtk_grid_new ());
650 g_object_set (G_OBJECT (grid),
652 "halign", GTK_ALIGN_FILL,
654 "valign", GTK_ALIGN_FILL,
660 priv->cert_hierarchy_treeview = add_scrolled_window (grid,
661 _("Certificate Hierarchy"), &row, gtk_tree_view_new ());
663 priv->cert_fields_treeview = add_scrolled_window (grid,
664 _("Certificate Fields"), &row, gtk_tree_view_new ());
666 priv->cert_field_value_textview = add_scrolled_window (grid,
667 _("Field Value"), &row, gtk_text_view_new ());
669 widget = gtk_label_new (_("Details"));
670 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (grid), widget);
672 gtk_widget_show_all (notebook);
674 fill_general_page (priv);
675 fill_details_page (priv);