32c8e7b0c3d1cbbeabed1a3ca92d1512b13aa3dc
[claws.git] / src / gtk / sslcertwindow.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2019 Colin Leroy <colin@colino.net>
4  * and the Claws Mail team
5  *
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.
10  *
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.
15  *
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/>.
18  * 
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #ifdef USE_GNUTLS
27
28 #include <gnutls/gnutls.h>
29 #include <gnutls/x509.h>
30 #include <gnutls/crypto.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <string.h>
36
37 #include <glib.h>
38 #include <glib/gi18n.h>
39 #include <gtk/gtk.h>
40
41 #include "prefs_common.h"
42 #include "defs.h"
43 #include "ssl_certificate.h"
44 #include "utils.h"
45 #include "alertpanel.h"
46 #include "hooks.h"
47
48 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert);
49 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert);
50 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert);
51
52 static GtkWidget *cert_presenter(SSLCertificate *cert)
53 {
54         GtkWidget *vbox = NULL;
55         GtkWidget *hbox = NULL;
56         GtkWidget *frame_owner = NULL;
57         GtkWidget *frame_signer = NULL;
58         GtkWidget *frame_status = NULL;
59         GtkTable *owner_table = NULL;
60         GtkTable *signer_table = NULL;
61         GtkTable *status_table = NULL;
62         GtkWidget *label = NULL;
63         
64         char *issuer_commonname, *issuer_location, *issuer_organization;
65         char *subject_commonname, *subject_location, *subject_organization;
66         char *sig_status, *exp_date;
67         char *sha1_fingerprint, *sha256_fingerprint, *fingerprint;
68         size_t n;
69         char buf[100];
70         unsigned char md[128];  
71         char *tmp;
72         time_t exp_time_t;
73         struct tm lt;
74         guint ret;
75
76         /* issuer */    
77         issuer_commonname = g_malloc(BUFFSIZE);
78         issuer_location = g_malloc(BUFFSIZE);
79         issuer_organization = g_malloc(BUFFSIZE);
80         subject_commonname = g_malloc(BUFFSIZE);
81         subject_location = g_malloc(BUFFSIZE);
82         subject_organization = g_malloc(BUFFSIZE);
83
84         n = BUFFSIZE;
85         if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert, 
86                 GNUTLS_OID_X520_COMMON_NAME, 0, 0, issuer_commonname, &n))
87                 strncpy(issuer_commonname, _("<not in certificate>"), BUFFSIZE);
88         n = BUFFSIZE;
89
90         if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert, 
91                 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, issuer_location, &n)) {
92                 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert, 
93                         GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, issuer_location, &n)) {
94                         strncpy(issuer_location, _("<not in certificate>"), BUFFSIZE);
95                 }
96         } else {
97                 tmp = g_malloc(BUFFSIZE);
98                 if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert, 
99                         GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
100                         strncat(issuer_location, ", ", BUFFSIZE-strlen(issuer_location)-1);
101                         strncat(issuer_location, tmp, BUFFSIZE-strlen(issuer_location)-1);
102                 }
103                 g_free(tmp);
104         }
105
106         n = BUFFSIZE;
107         if (gnutls_x509_crt_get_issuer_dn_by_oid(cert->x509_cert, 
108                 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, issuer_organization, &n))
109                 strncpy(issuer_organization, _("<not in certificate>"), BUFFSIZE);
110
111         n = BUFFSIZE;
112         if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert, 
113                 GNUTLS_OID_X520_COMMON_NAME, 0, 0, subject_commonname, &n))
114                 strncpy(subject_commonname, _("<not in certificate>"), BUFFSIZE);
115         n = BUFFSIZE;
116
117         if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert, 
118                 GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, subject_location, &n)) {
119                 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert, 
120                         GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, subject_location, &n)) {
121                         strncpy(subject_location, _("<not in certificate>"), BUFFSIZE);
122                 }
123         } else {
124                 tmp = g_malloc(BUFFSIZE);
125                 if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert, 
126                         GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, tmp, &n) == 0) {
127                         strncat(subject_location, ", ", BUFFSIZE-strlen(subject_location)-1);
128                         strncat(subject_location, tmp, BUFFSIZE-strlen(subject_location)-1);
129                 }
130                 g_free(tmp);
131         }
132
133         n = BUFFSIZE;
134         if (gnutls_x509_crt_get_dn_by_oid(cert->x509_cert, 
135                 GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, subject_organization, &n))
136                 strncpy(subject_organization, _("<not in certificate>"), BUFFSIZE);
137                 
138         exp_time_t = gnutls_x509_crt_get_expiration_time(cert->x509_cert);
139
140         memset(buf, 0, sizeof(buf));
141         if (exp_time_t > 0) {
142                 fast_strftime(buf, sizeof(buf)-1, prefs_common.date_format, localtime_r(&exp_time_t, &lt));
143                 exp_date = (*buf) ? g_strdup(buf):g_strdup("?");
144         } else
145                 exp_date = g_strdup("");
146
147         /* fingerprints */
148         n = 0;
149         memset(md, 0, sizeof(md));
150         if ((ret = gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA1, md, &n)) == GNUTLS_E_SHORT_MEMORY_BUFFER) {
151                 if (n <= sizeof(md))
152                         ret = gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA1, md, &n);
153         }
154
155         if (ret != 0) {
156                 g_warning("failed to obtain SHA1 fingerprint: %d", ret);
157                 sha1_fingerprint = g_strdup("-");
158         } else {
159                 sha1_fingerprint = readable_fingerprint(md, (int)n);
160         }
161
162         n = 0;
163         memset(md, 0, sizeof(md));
164         if ((ret = gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA256, md, &n)) == GNUTLS_E_SHORT_MEMORY_BUFFER) {
165                 if (n <= sizeof(md))
166                         ret = gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_SHA256, md, &n);
167         }
168
169         if (ret != 0) {
170                 g_warning("failed to obtain SHA256 fingerprint: %d", ret);
171                 sha256_fingerprint = g_strdup("-");
172         } else {
173                 sha256_fingerprint = readable_fingerprint(md, (int)n);
174         }
175
176
177         /* signature */
178         sig_status = ssl_certificate_check_signer(cert, cert->status);
179
180         if (sig_status==NULL)
181                 sig_status = g_strdup_printf(_("Correct%s"),exp_time_t < time(NULL)? _(" (expired)"): "");
182         else if (exp_time_t < time(NULL))
183                           sig_status = g_strconcat(sig_status,_(" (expired)"),NULL);
184
185         vbox = gtk_vbox_new(FALSE, 5);
186         hbox = gtk_hbox_new(FALSE, 5);
187         
188         frame_owner  = gtk_frame_new(_("Owner"));
189         frame_signer = gtk_frame_new(_("Signer"));
190         frame_status = gtk_frame_new(_("Status"));
191         
192         owner_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
193         signer_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
194         status_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
195         
196         label = gtk_label_new(_("Name: "));
197         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
198         gtk_table_attach(owner_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
199         label = gtk_label_new(subject_commonname);
200         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
201         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
202         gtk_table_attach(owner_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
203         
204         label = gtk_label_new(_("Organization: "));
205         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
206         gtk_table_attach(owner_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
207         label = gtk_label_new(subject_organization);
208         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
209         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
210         gtk_table_attach(owner_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
211         
212         label = gtk_label_new(_("Location: "));
213         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
214         gtk_table_attach(owner_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
215         label = gtk_label_new(subject_location);
216         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
217         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
218         gtk_table_attach(owner_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
219
220         label = gtk_label_new(_("Name: "));
221         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
222         gtk_table_attach(signer_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
223         label = gtk_label_new(issuer_commonname);
224         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
225         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
226         gtk_table_attach(signer_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
227         
228         label = gtk_label_new(_("Organization: "));
229         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
230         gtk_table_attach(signer_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
231         label = gtk_label_new(issuer_organization);
232         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
233         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
234         gtk_table_attach(signer_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
235         
236         label = gtk_label_new(_("Location: "));
237         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
238         gtk_table_attach(signer_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
239         label = gtk_label_new(issuer_location);
240         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
241         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
242         gtk_table_attach(signer_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
243
244         label = gtk_label_new(_("Fingerprint: \n"));
245         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
246         gtk_table_attach(status_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
247         fingerprint = g_strdup_printf("SHA1: %s\nSHA256: %s",
248                                       sha1_fingerprint, sha256_fingerprint);
249         label = gtk_label_new(fingerprint);
250         g_free(fingerprint);
251         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
252         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
253         gtk_table_attach(status_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
254         label = gtk_label_new(_("Signature status: "));
255         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
256         gtk_table_attach(status_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
257         label = gtk_label_new(sig_status);
258         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
259         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
260         gtk_table_attach(status_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
261         label = gtk_label_new(exp_time_t < time(NULL)? _("Expired on: "): _("Expires on: "));
262         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
263         gtk_table_attach(status_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
264         label = gtk_label_new(exp_date);
265         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
266         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
267         gtk_table_attach(status_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
268         
269         gtk_container_add(GTK_CONTAINER(frame_owner), GTK_WIDGET(owner_table));
270         gtk_container_add(GTK_CONTAINER(frame_signer), GTK_WIDGET(signer_table));
271         gtk_container_add(GTK_CONTAINER(frame_status), GTK_WIDGET(status_table));
272         
273         gtk_box_pack_end(GTK_BOX(hbox), frame_signer, TRUE, TRUE, 0);
274         gtk_box_pack_end(GTK_BOX(hbox), frame_owner, TRUE, TRUE, 0);
275         gtk_box_pack_end(GTK_BOX(vbox), frame_status, TRUE, TRUE, 0);
276         gtk_box_pack_end(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
277         
278         gtk_widget_show_all(vbox);
279         
280         g_free(issuer_commonname);
281         g_free(issuer_location);
282         g_free(issuer_organization);
283         g_free(subject_commonname);
284         g_free(subject_location);
285         g_free(subject_organization);
286         g_free(sha1_fingerprint);
287         g_free(sha256_fingerprint);
288         g_free(sig_status);
289         g_free(exp_date);
290         return vbox;
291 }
292
293 static gboolean sslcert_ask_hook(gpointer source, gpointer data)
294 {
295         SSLCertHookData *hookdata = (SSLCertHookData *)source;
296
297         if (hookdata == NULL) {
298                 return FALSE;
299         }
300         
301         if (prefs_common.skip_ssl_cert_check) {
302                 hookdata->accept = TRUE;
303                 return TRUE;
304         }
305
306         if (hookdata->old_cert == NULL) {
307                 if (hookdata->expired)
308                         hookdata->accept = sslcertwindow_ask_expired_cert(hookdata->cert);
309                 else
310                         hookdata->accept = sslcertwindow_ask_new_cert(hookdata->cert);
311         } else {
312                 hookdata->accept = sslcertwindow_ask_changed_cert(hookdata->old_cert, hookdata->cert);
313         }
314
315         return TRUE;
316 }
317
318 void sslcertwindow_register_hook(void)
319 {
320         hooks_register_hook(SSLCERT_ASK_HOOKLIST, sslcert_ask_hook, NULL);
321 }
322
323 void sslcertwindow_show_cert(SSLCertificate *cert)
324 {
325         GtkWidget *cert_widget = cert_presenter(cert);
326         gchar *buf;
327         
328         buf = g_strdup_printf(_("SSL/TLS certificate for %s"), cert->host);
329         alertpanel_full(buf, NULL, GTK_STOCK_CLOSE, NULL, NULL,
330                         ALERTFOCUS_FIRST, FALSE, cert_widget, ALERT_NOTICE);
331         g_free(buf);
332 }
333
334 static gchar *sslcertwindow_get_invalid_str(SSLCertificate *cert)
335 {
336         gchar *subject_cn = NULL;
337         gchar *str = NULL;
338
339         if (ssl_certificate_check_subject_cn(cert))
340                 return g_strdup("");
341         
342         subject_cn = ssl_certificate_get_subject_cn(cert);
343         
344         str = g_strdup_printf(_("Certificate is for %s, but connection is to %s.\n"
345                                 "You may be connecting to a rogue server.\n\n"), 
346                                 subject_cn, cert->host);
347         g_free(subject_cn);
348         
349         return str;
350 }
351
352 static gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert)
353 {
354         gchar *buf, *sig_status;
355         AlertValue val;
356         GtkWidget *vbox;
357         GtkWidget *label;
358         GtkWidget *button;
359         GtkWidget *cert_widget;
360         gchar *invalid_str = sslcertwindow_get_invalid_str(cert);
361         const gchar *title;
362
363         vbox = gtk_vbox_new(FALSE, 5);
364         buf = g_strdup_printf(_("Certificate for %s is unknown.\n%sDo you want to accept it?"), cert->host, invalid_str);
365         g_free(invalid_str);
366
367         label = gtk_label_new(buf);
368         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
369         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
370         g_free(buf);
371         
372         sig_status = ssl_certificate_check_signer(cert, cert->status);
373         if (sig_status==NULL)
374                 sig_status = g_strdup(_("Correct"));
375
376         buf = g_strdup_printf(_("Signature status: %s"), sig_status);
377         label = gtk_label_new(buf);
378         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
379         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
380         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
381         g_free(buf);
382         g_free(sig_status);
383         
384         button = gtk_expander_new_with_mnemonic(_("_View certificate"));
385         gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
386         cert_widget = cert_presenter(cert);
387         gtk_container_add(GTK_CONTAINER(button), cert_widget);
388
389         if (!ssl_certificate_check_subject_cn(cert))
390                 title = _("SSL/TLS certificate is invalid");
391         else
392                 title = _("SSL/TLS certificate is unknown");
393
394         val = alertpanel_full(title, NULL,
395                               _("_Cancel connection"), _("_Accept and save"), NULL,
396                               ALERTFOCUS_FIRST, FALSE, vbox, ALERT_QUESTION);
397         
398         return (val == G_ALERTALTERNATE);
399 }
400
401 static gboolean sslcertwindow_ask_expired_cert(SSLCertificate *cert)
402 {
403         gchar *buf, *sig_status;
404         AlertValue val;
405         GtkWidget *vbox;
406         GtkWidget *label;
407         GtkWidget *button;
408         GtkWidget *cert_widget;
409         gchar *invalid_str = sslcertwindow_get_invalid_str(cert);
410         const gchar *title;
411
412         vbox = gtk_vbox_new(FALSE, 5);
413         buf = g_strdup_printf(_("Certificate for %s is expired.\n%sDo you want to continue?"), cert->host, invalid_str);
414         g_free(invalid_str);
415
416         label = gtk_label_new(buf);
417         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
418         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
419         g_free(buf);
420         
421         sig_status = ssl_certificate_check_signer(cert, cert->status);
422
423         if (sig_status==NULL)
424                 sig_status = g_strdup(_("Correct"));
425
426         buf = g_strdup_printf(_("Signature status: %s"), sig_status);
427         label = gtk_label_new(buf);
428         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
429         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
430         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
431         g_free(buf);
432         g_free(sig_status);
433         
434         button = gtk_expander_new_with_mnemonic(_("_View certificate"));
435         gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
436         cert_widget = cert_presenter(cert);
437         gtk_container_add(GTK_CONTAINER(button), cert_widget);
438
439         if (!ssl_certificate_check_subject_cn(cert))
440                 title = _("SSL/TLS certificate is invalid and expired");
441         else
442                 title = _("SSL/TLS certificate is expired");
443
444         val = alertpanel_full(title, NULL,
445                               _("_Cancel connection"), _("_Accept"), NULL,
446                               ALERTFOCUS_FIRST, FALSE, vbox, ALERT_QUESTION);
447         
448         return (val == G_ALERTALTERNATE);
449 }
450
451 static gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert)
452 {
453         GtkWidget *old_cert_widget = cert_presenter(old_cert);
454         GtkWidget *new_cert_widget = cert_presenter(new_cert);
455         GtkWidget *vbox;
456         gchar *buf, *sig_status;
457         GtkWidget *vbox2;
458         GtkWidget *label;
459         GtkWidget *button;
460         AlertValue val;
461         gchar *invalid_str = sslcertwindow_get_invalid_str(new_cert);
462         const gchar *title;
463
464         vbox = gtk_vbox_new(FALSE, 5);
465         label = gtk_label_new(_("New certificate:"));
466         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
467         gtk_box_pack_end(GTK_BOX(vbox), new_cert_widget, TRUE, TRUE, 0);
468         gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
469         gtk_box_pack_end(GTK_BOX(vbox), gtk_hseparator_new(), TRUE, TRUE, 0);
470         label = gtk_label_new(_("Known certificate:"));
471         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
472         gtk_box_pack_end(GTK_BOX(vbox), old_cert_widget, TRUE, TRUE, 0);
473         gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
474         gtk_widget_show_all(vbox);
475         
476         vbox2 = gtk_vbox_new(FALSE, 5);
477         buf = g_strdup_printf(_("Certificate for %s has changed.\n%sDo you want to accept it?"), new_cert->host, invalid_str);
478         g_free(invalid_str);
479
480         label = gtk_label_new(buf);
481         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
482         gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
483         g_free(buf);
484         
485         sig_status = ssl_certificate_check_signer(new_cert, new_cert->status);
486
487         if (sig_status==NULL)
488                 sig_status = g_strdup(_("Correct"));
489
490         buf = g_strdup_printf(_("Signature status: %s"), sig_status);
491         label = gtk_label_new(buf);
492         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
493         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
494         gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
495         g_free(buf);
496         g_free(sig_status);
497         
498         button = gtk_expander_new_with_mnemonic(_("_View certificates"));
499         gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
500         gtk_container_add(GTK_CONTAINER(button), vbox);
501
502         if (!ssl_certificate_check_subject_cn(new_cert))
503                 title = _("SSL/TLS certificate changed and is invalid");
504         else
505                 title = _("SSL/TLS certificate changed");
506         val = alertpanel_full(title, NULL,
507                               _("_Cancel connection"), _("_Accept and save"), NULL,
508                               ALERTFOCUS_FIRST, FALSE, vbox2, ALERT_WARNING);
509         
510         return (val == G_ALERTALTERNATE);
511 }
512 #endif