2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Colin Leroy <colin@colino.net>
4 * and the Claws Mail team
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
28 #include <openssl/ssl.h>
30 #include <gnutls/gnutls.h>
31 #include <gnutls/x509.h>
32 #include <sys/types.h>
39 #include <glib/gi18n.h>
42 #include "prefs_common.h"
44 #include "ssl_certificate.h"
46 #include "alertpanel.h"
49 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert);
50 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert);
51 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert);
53 static GtkWidget *cert_presenter(SSLCertificate *cert)
55 GtkWidget *vbox = NULL;
56 GtkWidget *hbox = NULL;
57 GtkWidget *frame_owner = NULL;
58 GtkWidget *frame_signer = NULL;
59 GtkWidget *frame_status = NULL;
60 GtkTable *owner_table = NULL;
61 GtkTable *signer_table = NULL;
62 GtkTable *status_table = NULL;
63 GtkWidget *label = NULL;
65 char *issuer_commonname, *issuer_location, *issuer_organization;
66 char *subject_commonname, *subject_location, *subject_organization;
67 char *sig_status, *exp_date;
68 char *md5_fingerprint, *sha1_fingerprint, *fingerprint;
75 unsigned char md[128];
86 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
87 NID_commonName, buf, 100) >= 0)
88 issuer_commonname = g_strdup(buf);
90 issuer_commonname = g_strdup(_("<not in certificate>"));
91 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
92 NID_localityName, buf, 100) >= 0) {
93 issuer_location = g_strdup(buf);
94 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
95 NID_countryName, buf, 100) >= 0)
96 issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
97 } else if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
98 NID_countryName, buf, 100) >= 0)
99 issuer_location = g_strdup(buf);
101 issuer_location = g_strdup(_("<not in certificate>"));
103 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
104 NID_organizationName, buf, 100) >= 0)
105 issuer_organization = g_strdup(buf);
107 issuer_organization = g_strdup(_("<not in certificate>"));
110 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
111 NID_commonName, buf, 100) >= 0)
112 subject_commonname = g_strdup(buf);
114 subject_commonname = g_strdup(_("<not in certificate>"));
115 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
116 NID_localityName, buf, 100) >= 0) {
117 subject_location = g_strdup(buf);
118 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
119 NID_countryName, buf, 100) >= 0)
120 subject_location = g_strconcat(subject_location,", ",buf, NULL);
121 } else if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
122 NID_countryName, buf, 100) >= 0)
123 subject_location = g_strdup(buf);
125 subject_location = g_strdup(_("<not in certificate>"));
127 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
128 NID_organizationName, buf, 100) >= 0)
129 subject_organization = g_strdup(buf);
131 subject_organization = g_strdup(_("<not in certificate>"));
133 if ((validity = X509_get_notAfter(cert->x509_cert)) != NULL) {
134 exp_time_t = asn1toTime(validity);
136 exp_time_t = (time_t)0;
139 issuer_commonname = g_malloc(BUFFSIZE);
140 issuer_location = g_malloc(BUFFSIZE);
141 issuer_organization = g_malloc(BUFFSIZE);
142 subject_commonname = g_malloc(BUFFSIZE);
143 subject_location = g_malloc(BUFFSIZE);
144 subject_organization = g_malloc(BUFFSIZE);
147 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
148 GNUTLS_OID_X520_COMMON_NAME, 0, 0, issuer_commonname, &n))
149 strncpy(issuer_commonname, _("<not in certificate>"), BUFFSIZE);
152 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
153 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, issuer_location, &n)) {
154 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
155 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, issuer_location, &n)) {
156 strncpy(issuer_location, _("<not in certificate>"), BUFFSIZE);
159 tmp = g_malloc(BUFFSIZE);
160 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
161 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
162 strncat(issuer_location, ", ", BUFFSIZE-strlen(issuer_location));
163 strncat(issuer_location, tmp, BUFFSIZE-strlen(issuer_location));
169 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
170 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, issuer_organization, &n))
171 strncpy(issuer_organization, _("<not in certificate>"), BUFFSIZE);
174 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
175 GNUTLS_OID_X520_COMMON_NAME, 0, 0, subject_commonname, &n))
176 strncpy(subject_commonname, _("<not in certificate>"), BUFFSIZE);
179 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
180 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, subject_location, &n)) {
181 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
182 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, subject_location, &n)) {
183 strncpy(subject_location, _("<not in certificate>"), BUFFSIZE);
186 tmp = g_malloc(BUFFSIZE);
187 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
188 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
189 strncat(subject_location, ", ", BUFFSIZE-strlen(subject_location));
190 strncat(subject_location, tmp, BUFFSIZE-strlen(subject_location));
196 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
197 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, subject_organization, &n))
198 strncpy(subject_organization, _("<not in certificate>"), BUFFSIZE);
200 exp_time_t = gnutls_x509_crt_get_expiration_time(cert->x509_cert);
203 memset(buf, 0, sizeof(buf));
204 if (exp_time_t > 0) {
205 strftime(buf, sizeof(buf)-1, prefs_common.date_format, localtime_r(&exp_time_t, <));
206 exp_date = (*buf) ? g_strdup(buf):g_strdup("?");
208 exp_date = g_strdup("");
212 X509_digest(cert->x509_cert, EVP_md5(), md, &n);
213 md5_fingerprint = readable_fingerprint(md, (int)n);
214 X509_digest(cert->x509_cert, EVP_sha1(), md, &n);
215 sha1_fingerprint = readable_fingerprint(md, (int)n);
218 sig_status = ssl_certificate_check_signer(cert->x509_cert);
221 gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_MD5, md, &n);
222 md5_fingerprint = readable_fingerprint(md, (int)n);
224 gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA1, md, &n);
225 sha1_fingerprint = readable_fingerprint(md, (int)n);
228 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
231 if (sig_status==NULL)
232 sig_status = g_strdup(_("correct"));
234 vbox = gtk_vbox_new(FALSE, 5);
235 hbox = gtk_hbox_new(FALSE, 5);
237 frame_owner = gtk_frame_new(_("Owner"));
238 frame_signer = gtk_frame_new(_("Signer"));
239 frame_status = gtk_frame_new(_("Status"));
241 owner_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
242 signer_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
243 status_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
245 label = gtk_label_new(_("Name: "));
246 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
247 gtk_table_attach(owner_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
248 label = gtk_label_new(subject_commonname);
249 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
250 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
251 gtk_table_attach(owner_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
253 label = gtk_label_new(_("Organization: "));
254 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
255 gtk_table_attach(owner_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
256 label = gtk_label_new(subject_organization);
257 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
258 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
259 gtk_table_attach(owner_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
261 label = gtk_label_new(_("Location: "));
262 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
263 gtk_table_attach(owner_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
264 label = gtk_label_new(subject_location);
265 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
266 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
267 gtk_table_attach(owner_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
269 label = gtk_label_new(_("Name: "));
270 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
271 gtk_table_attach(signer_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
272 label = gtk_label_new(issuer_commonname);
273 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
274 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
275 gtk_table_attach(signer_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
277 label = gtk_label_new(_("Organization: "));
278 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
279 gtk_table_attach(signer_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
280 label = gtk_label_new(issuer_organization);
281 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
282 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
283 gtk_table_attach(signer_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
285 label = gtk_label_new(_("Location: "));
286 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
287 gtk_table_attach(signer_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
288 label = gtk_label_new(issuer_location);
289 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
290 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
291 gtk_table_attach(signer_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
293 label = gtk_label_new(_("Fingerprint: \n"));
294 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
295 gtk_table_attach(status_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
296 fingerprint = g_strdup_printf("MD5: %s\nSHA1: %s",
297 md5_fingerprint, sha1_fingerprint);
298 label = gtk_label_new(fingerprint);
300 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
301 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
302 gtk_table_attach(status_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
303 label = gtk_label_new(_("Signature status: "));
304 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
305 gtk_table_attach(status_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
306 label = gtk_label_new(sig_status);
307 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
308 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
309 gtk_table_attach(status_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
310 label = gtk_label_new(_("Expires on: "));
311 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
312 gtk_table_attach(status_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
313 label = gtk_label_new(exp_date);
314 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
315 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
316 gtk_table_attach(status_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
318 gtk_container_add(GTK_CONTAINER(frame_owner), GTK_WIDGET(owner_table));
319 gtk_container_add(GTK_CONTAINER(frame_signer), GTK_WIDGET(signer_table));
320 gtk_container_add(GTK_CONTAINER(frame_status), GTK_WIDGET(status_table));
322 gtk_box_pack_end(GTK_BOX(hbox), frame_signer, TRUE, TRUE, 0);
323 gtk_box_pack_end(GTK_BOX(hbox), frame_owner, TRUE, TRUE, 0);
324 gtk_box_pack_end(GTK_BOX(vbox), frame_status, TRUE, TRUE, 0);
325 gtk_box_pack_end(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
327 gtk_widget_show_all(vbox);
329 g_free(issuer_commonname);
330 g_free(issuer_location);
331 g_free(issuer_organization);
332 g_free(subject_commonname);
333 g_free(subject_location);
334 g_free(subject_organization);
335 g_free(md5_fingerprint);
336 g_free(sha1_fingerprint);
342 static gboolean sslcert_ask_hook(gpointer source, gpointer data)
344 SSLCertHookData *hookdata = (SSLCertHookData *)source;
346 if (prefs_common.skip_ssl_cert_check)
349 if (hookdata == NULL) {
352 if (hookdata->old_cert == NULL) {
353 if (hookdata->expired)
354 hookdata->accept = sslcertwindow_ask_expired_cert(hookdata->cert);
356 hookdata->accept = sslcertwindow_ask_new_cert(hookdata->cert);
358 hookdata->accept = sslcertwindow_ask_changed_cert(hookdata->old_cert, hookdata->cert);
363 void sslcertwindow_register_hook(void)
365 hooks_register_hook(SSLCERT_ASK_HOOKLIST, sslcert_ask_hook, NULL);
368 void sslcertwindow_show_cert(SSLCertificate *cert)
370 GtkWidget *cert_widget = cert_presenter(cert);
373 buf = g_strdup_printf(_("SSL certificate for %s"), cert->host);
374 alertpanel_full(buf, NULL, GTK_STOCK_CLOSE, NULL, NULL,
375 FALSE, cert_widget, ALERT_NOTICE, G_ALERTDEFAULT);
379 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert)
381 gchar *buf, *sig_status;
386 GtkWidget *cert_widget;
388 vbox = gtk_vbox_new(FALSE, 5);
389 buf = g_strdup_printf(_("Certificate for %s is unknown.\nDo you want to accept it?"), cert->host);
390 label = gtk_label_new(buf);
391 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
392 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
396 sig_status = ssl_certificate_check_signer(cert->x509_cert);
398 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
400 if (sig_status==NULL)
401 sig_status = g_strdup(_("correct"));
403 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
404 label = gtk_label_new(buf);
405 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
406 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
407 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
411 button = gtk_expander_new_with_mnemonic(_("_View certificate"));
412 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
413 cert_widget = cert_presenter(cert);
414 gtk_container_add(GTK_CONTAINER(button), cert_widget);
416 val = alertpanel_full(_("Unknown SSL Certificate"), NULL,
417 _("_Cancel connection"), _("_Accept and save"), NULL,
418 FALSE, vbox, ALERT_QUESTION, G_ALERTDEFAULT);
420 return (val == G_ALERTALTERNATE);
423 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert)
425 gchar *buf, *sig_status;
430 GtkWidget *cert_widget;
432 vbox = gtk_vbox_new(FALSE, 5);
433 buf = g_strdup_printf(_("Certificate for %s is expired.\nDo you want to continue?"), cert->host);
434 label = gtk_label_new(buf);
435 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
436 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
440 sig_status = ssl_certificate_check_signer(cert->x509_cert);
442 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
445 if (sig_status==NULL)
446 sig_status = g_strdup(_("correct"));
448 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
449 label = gtk_label_new(buf);
450 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
451 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
452 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
456 button = gtk_expander_new_with_mnemonic(_("_View certificate"));
457 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
458 cert_widget = cert_presenter(cert);
459 gtk_container_add(GTK_CONTAINER(button), cert_widget);
461 val = alertpanel_full(_("Expired SSL Certificate"), NULL,
462 _("_Cancel connection"), _("_Accept"), NULL,
463 FALSE, vbox, ALERT_QUESTION, G_ALERTDEFAULT);
465 return (val == G_ALERTALTERNATE);
468 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert)
470 GtkWidget *old_cert_widget = cert_presenter(old_cert);
471 GtkWidget *new_cert_widget = cert_presenter(new_cert);
473 gchar *buf, *sig_status;
479 vbox = gtk_vbox_new(FALSE, 5);
480 label = gtk_label_new(_("New certificate:"));
481 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
482 gtk_box_pack_end(GTK_BOX(vbox), new_cert_widget, TRUE, TRUE, 0);
483 gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
484 gtk_box_pack_end(GTK_BOX(vbox), gtk_hseparator_new(), TRUE, TRUE, 0);
485 label = gtk_label_new(_("Known certificate:"));
486 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
487 gtk_box_pack_end(GTK_BOX(vbox), old_cert_widget, TRUE, TRUE, 0);
488 gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
489 gtk_widget_show_all(vbox);
491 vbox2 = gtk_vbox_new(FALSE, 5);
492 buf = g_strdup_printf(_("Certificate for %s has changed. Do you want to accept it?"), new_cert->host);
493 label = gtk_label_new(buf);
494 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
495 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
499 sig_status = ssl_certificate_check_signer(new_cert->x509_cert);
501 sig_status = ssl_certificate_check_signer(new_cert->x509_cert, new_cert->status);
504 if (sig_status==NULL)
505 sig_status = g_strdup(_("correct"));
507 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
508 label = gtk_label_new(buf);
509 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
510 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
511 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
515 button = gtk_expander_new_with_mnemonic(_("_View certificates"));
516 gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
517 gtk_container_add(GTK_CONTAINER(button), vbox);
519 val = alertpanel_full(_("Changed SSL Certificate"), NULL,
520 _("_Cancel connection"), _("_Accept and save"), NULL,
521 FALSE, vbox2, ALERT_WARNING, G_ALERTDEFAULT);
523 return (val == G_ALERTALTERNATE);