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/>.
27 #include <gnutls/gnutls.h>
28 #include <gnutls/x509.h>
29 #include <sys/types.h>
36 #include <glib/gi18n.h>
39 #include "prefs_common.h"
41 #include "ssl_certificate.h"
43 #include "alertpanel.h"
46 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert);
47 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert);
48 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert);
50 static GtkWidget *cert_presenter(SSLCertificate *cert)
52 GtkWidget *vbox = NULL;
53 GtkWidget *hbox = NULL;
54 GtkWidget *frame_owner = NULL;
55 GtkWidget *frame_signer = NULL;
56 GtkWidget *frame_status = NULL;
57 GtkTable *owner_table = NULL;
58 GtkTable *signer_table = NULL;
59 GtkTable *status_table = NULL;
60 GtkWidget *label = NULL;
62 char *issuer_commonname, *issuer_location, *issuer_organization;
63 char *subject_commonname, *subject_location, *subject_organization;
64 char *sig_status, *exp_date;
65 char *md5_fingerprint, *sha1_fingerprint, *fingerprint;
68 unsigned char md[128];
74 issuer_commonname = g_malloc(BUFFSIZE);
75 issuer_location = g_malloc(BUFFSIZE);
76 issuer_organization = g_malloc(BUFFSIZE);
77 subject_commonname = g_malloc(BUFFSIZE);
78 subject_location = g_malloc(BUFFSIZE);
79 subject_organization = g_malloc(BUFFSIZE);
82 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
83 GNUTLS_OID_X520_COMMON_NAME, 0, 0, issuer_commonname, &n))
84 strncpy(issuer_commonname, _("<not in certificate>"), BUFFSIZE);
87 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
88 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, issuer_location, &n)) {
89 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
90 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, issuer_location, &n)) {
91 strncpy(issuer_location, _("<not in certificate>"), BUFFSIZE);
94 tmp = g_malloc(BUFFSIZE);
95 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
96 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
97 strncat(issuer_location, ", ", BUFFSIZE-strlen(issuer_location));
98 strncat(issuer_location, tmp, BUFFSIZE-strlen(issuer_location));
104 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert,
105 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, issuer_organization, &n))
106 strncpy(issuer_organization, _("<not in certificate>"), BUFFSIZE);
109 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
110 GNUTLS_OID_X520_COMMON_NAME, 0, 0, subject_commonname, &n))
111 strncpy(subject_commonname, _("<not in certificate>"), BUFFSIZE);
114 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
115 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, subject_location, &n)) {
116 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
117 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, subject_location, &n)) {
118 strncpy(subject_location, _("<not in certificate>"), BUFFSIZE);
121 tmp = g_malloc(BUFFSIZE);
122 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
123 GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
124 strncat(subject_location, ", ", BUFFSIZE-strlen(subject_location));
125 strncat(subject_location, tmp, BUFFSIZE-strlen(subject_location));
131 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
132 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, subject_organization, &n))
133 strncpy(subject_organization, _("<not in certificate>"), BUFFSIZE);
135 exp_time_t = gnutls_x509_crt_get_expiration_time(cert->x509_cert);
137 memset(buf, 0, sizeof(buf));
138 if (exp_time_t > 0) {
139 strftime(buf, sizeof(buf)-1, prefs_common.date_format, localtime_r(&exp_time_t, <));
140 exp_date = (*buf) ? g_strdup(buf):g_strdup("?");
142 exp_date = g_strdup("");
146 gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_MD5, md, &n);
147 md5_fingerprint = readable_fingerprint(md, (int)n);
149 gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA1, md, &n);
150 sha1_fingerprint = readable_fingerprint(md, (int)n);
153 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
155 if (sig_status==NULL)
156 sig_status = g_strdup(_("Correct"));
158 vbox = gtk_vbox_new(FALSE, 5);
159 hbox = gtk_hbox_new(FALSE, 5);
161 frame_owner = gtk_frame_new(_("Owner"));
162 frame_signer = gtk_frame_new(_("Signer"));
163 frame_status = gtk_frame_new(_("Status"));
165 owner_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
166 signer_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
167 status_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
169 label = gtk_label_new(_("Name: "));
170 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
171 gtk_table_attach(owner_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
172 label = gtk_label_new(subject_commonname);
173 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
174 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
175 gtk_table_attach(owner_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
177 label = gtk_label_new(_("Organization: "));
178 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
179 gtk_table_attach(owner_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
180 label = gtk_label_new(subject_organization);
181 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
182 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
183 gtk_table_attach(owner_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
185 label = gtk_label_new(_("Location: "));
186 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
187 gtk_table_attach(owner_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
188 label = gtk_label_new(subject_location);
189 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
190 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
191 gtk_table_attach(owner_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
193 label = gtk_label_new(_("Name: "));
194 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
195 gtk_table_attach(signer_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
196 label = gtk_label_new(issuer_commonname);
197 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
198 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
199 gtk_table_attach(signer_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
201 label = gtk_label_new(_("Organization: "));
202 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
203 gtk_table_attach(signer_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
204 label = gtk_label_new(issuer_organization);
205 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
206 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
207 gtk_table_attach(signer_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
209 label = gtk_label_new(_("Location: "));
210 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
211 gtk_table_attach(signer_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
212 label = gtk_label_new(issuer_location);
213 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
214 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
215 gtk_table_attach(signer_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
217 label = gtk_label_new(_("Fingerprint: \n"));
218 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
219 gtk_table_attach(status_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
220 fingerprint = g_strdup_printf("MD5: %s\nSHA1: %s",
221 md5_fingerprint, sha1_fingerprint);
222 label = gtk_label_new(fingerprint);
224 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
225 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
226 gtk_table_attach(status_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
227 label = gtk_label_new(_("Signature status: "));
228 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
229 gtk_table_attach(status_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
230 label = gtk_label_new(sig_status);
231 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
232 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
233 gtk_table_attach(status_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
234 label = gtk_label_new(_("Expires on: "));
235 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
236 gtk_table_attach(status_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
237 label = gtk_label_new(exp_date);
238 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
239 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
240 gtk_table_attach(status_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
242 gtk_container_add(GTK_CONTAINER(frame_owner), GTK_WIDGET(owner_table));
243 gtk_container_add(GTK_CONTAINER(frame_signer), GTK_WIDGET(signer_table));
244 gtk_container_add(GTK_CONTAINER(frame_status), GTK_WIDGET(status_table));
246 gtk_box_pack_end(GTK_BOX(hbox), frame_signer, TRUE, TRUE, 0);
247 gtk_box_pack_end(GTK_BOX(hbox), frame_owner, TRUE, TRUE, 0);
248 gtk_box_pack_end(GTK_BOX(vbox), frame_status, TRUE, TRUE, 0);
249 gtk_box_pack_end(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
251 gtk_widget_show_all(vbox);
253 g_free(issuer_commonname);
254 g_free(issuer_location);
255 g_free(issuer_organization);
256 g_free(subject_commonname);
257 g_free(subject_location);
258 g_free(subject_organization);
259 g_free(md5_fingerprint);
260 g_free(sha1_fingerprint);
266 static gboolean sslcert_ask_hook(gpointer source, gpointer data)
268 SSLCertHookData *hookdata = (SSLCertHookData *)source;
270 if (prefs_common.skip_ssl_cert_check)
273 if (hookdata == NULL) {
276 if (hookdata->old_cert == NULL) {
277 if (hookdata->expired)
278 hookdata->accept = sslcertwindow_ask_expired_cert(hookdata->cert);
280 hookdata->accept = sslcertwindow_ask_new_cert(hookdata->cert);
282 hookdata->accept = sslcertwindow_ask_changed_cert(hookdata->old_cert, hookdata->cert);
287 void sslcertwindow_register_hook(void)
289 hooks_register_hook(SSLCERT_ASK_HOOKLIST, sslcert_ask_hook, NULL);
292 void sslcertwindow_show_cert(SSLCertificate *cert)
294 GtkWidget *cert_widget = cert_presenter(cert);
297 buf = g_strdup_printf(_("SSL certificate for %s"), cert->host);
298 alertpanel_full(buf, NULL, GTK_STOCK_CLOSE, NULL, NULL,
299 FALSE, cert_widget, ALERT_NOTICE, G_ALERTDEFAULT);
303 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert)
305 gchar *buf, *sig_status;
310 GtkWidget *cert_widget;
312 vbox = gtk_vbox_new(FALSE, 5);
313 buf = g_strdup_printf(_("Certificate for %s is unknown.\nDo you want to accept it?"), cert->host);
314 label = gtk_label_new(buf);
315 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
316 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
319 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
320 if (sig_status==NULL)
321 sig_status = g_strdup(_("Correct"));
323 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
324 label = gtk_label_new(buf);
325 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
326 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
327 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
331 button = gtk_expander_new_with_mnemonic(_("_View certificate"));
332 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
333 cert_widget = cert_presenter(cert);
334 gtk_container_add(GTK_CONTAINER(button), cert_widget);
336 val = alertpanel_full(_("Unknown SSL Certificate"), NULL,
337 _("_Cancel connection"), _("_Accept and save"), NULL,
338 FALSE, vbox, ALERT_QUESTION, G_ALERTDEFAULT);
340 return (val == G_ALERTALTERNATE);
343 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert)
345 gchar *buf, *sig_status;
350 GtkWidget *cert_widget;
352 vbox = gtk_vbox_new(FALSE, 5);
353 buf = g_strdup_printf(_("Certificate for %s is expired.\nDo you want to continue?"), cert->host);
354 label = gtk_label_new(buf);
355 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
356 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
359 sig_status = ssl_certificate_check_signer(cert->x509_cert, cert->status);
361 if (sig_status==NULL)
362 sig_status = g_strdup(_("Correct"));
364 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
365 label = gtk_label_new(buf);
366 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
367 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
368 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
372 button = gtk_expander_new_with_mnemonic(_("_View certificate"));
373 gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
374 cert_widget = cert_presenter(cert);
375 gtk_container_add(GTK_CONTAINER(button), cert_widget);
377 val = alertpanel_full(_("Expired SSL Certificate"), NULL,
378 _("_Cancel connection"), _("_Accept"), NULL,
379 FALSE, vbox, ALERT_QUESTION, G_ALERTDEFAULT);
381 return (val == G_ALERTALTERNATE);
384 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert)
386 GtkWidget *old_cert_widget = cert_presenter(old_cert);
387 GtkWidget *new_cert_widget = cert_presenter(new_cert);
389 gchar *buf, *sig_status;
395 vbox = gtk_vbox_new(FALSE, 5);
396 label = gtk_label_new(_("New certificate:"));
397 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
398 gtk_box_pack_end(GTK_BOX(vbox), new_cert_widget, TRUE, TRUE, 0);
399 gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
400 gtk_box_pack_end(GTK_BOX(vbox), gtk_hseparator_new(), TRUE, TRUE, 0);
401 label = gtk_label_new(_("Known certificate:"));
402 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
403 gtk_box_pack_end(GTK_BOX(vbox), old_cert_widget, TRUE, TRUE, 0);
404 gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
405 gtk_widget_show_all(vbox);
407 vbox2 = gtk_vbox_new(FALSE, 5);
408 buf = g_strdup_printf(_("Certificate for %s has changed. Do you want to accept it?"), new_cert->host);
409 label = gtk_label_new(buf);
410 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
411 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
414 sig_status = ssl_certificate_check_signer(new_cert->x509_cert, new_cert->status);
416 if (sig_status==NULL)
417 sig_status = g_strdup(_("Correct"));
419 buf = g_strdup_printf(_("Signature status: %s"), sig_status);
420 label = gtk_label_new(buf);
421 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
422 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
423 gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
427 button = gtk_expander_new_with_mnemonic(_("_View certificates"));
428 gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
429 gtk_container_add(GTK_CONTAINER(button), vbox);
431 val = alertpanel_full(_("Changed SSL Certificate"), NULL,
432 _("_Cancel connection"), _("_Accept and save"), NULL,
433 FALSE, vbox2, ALERT_WARNING, G_ALERTDEFAULT);
435 return (val == G_ALERTALTERNATE);