b9f7c309774d38ea3e7c16af70ee09531cb35b70
[claws.git] / src / gtk / sslcertwindow.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #if USE_OPENSSL
25
26 #include <openssl/ssl.h>
27 #include <glib.h>
28 #include <gtk/gtk.h>
29 #include "../common/intl.h"
30 #include "../common/ssl_certificate.h"
31 #include "../common/utils.h"
32 #include "../alertpanel.h"
33 #include "../common/hooks.h"
34
35 static void toggle_cert_cb(GtkWidget    *widget,
36                          gpointer        data);
37 gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert);
38 gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert);
39
40 GtkWidget *cert_presenter(SSLCertificate *cert)
41 {
42         GtkWidget *vbox = NULL;
43         GtkWidget *hbox = NULL;
44         GtkWidget *frame_owner = NULL;
45         GtkWidget *frame_signer = NULL;
46         GtkWidget *frame_status = NULL;
47         GtkTable *owner_table = NULL;
48         GtkTable *signer_table = NULL;
49         GtkTable *status_table = NULL;
50         GtkWidget *label = NULL;
51         char buf[100];
52         char *issuer_commonname, *issuer_location, *issuer_organization;
53         char *subject_commonname, *subject_location, *subject_organization;
54         char *fingerprint, *sig_status;
55         unsigned int n;
56         unsigned char md[EVP_MAX_MD_SIZE];      
57         
58         /* issuer */    
59         if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert), 
60                                        NID_commonName, buf, 100) >= 0)
61                 issuer_commonname = g_strdup(buf);
62         else
63                 issuer_commonname = g_strdup(_("<not in certificate>"));
64         if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert), 
65                                        NID_localityName, buf, 100) >= 0) {
66                 issuer_location = g_strdup(buf);
67                 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert), 
68                                        NID_countryName, buf, 100) >= 0)
69                         issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
70         } else if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert), 
71                                        NID_countryName, buf, 100) >= 0)
72                 issuer_location = g_strdup(buf);
73         else
74                 issuer_location = g_strdup(_("<not in certificate>"));
75
76         if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert), 
77                                        NID_organizationName, buf, 100) >= 0)
78                 issuer_organization = g_strdup(buf);
79         else 
80                 issuer_organization = g_strdup(_("<not in certificate>"));
81          
82         /* subject */   
83         if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert), 
84                                        NID_commonName, buf, 100) >= 0)
85                 subject_commonname = g_strdup(buf);
86         else
87                 subject_commonname = g_strdup(_("<not in certificate>"));
88         if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert), 
89                                        NID_localityName, buf, 100) >= 0) {
90                 subject_location = g_strdup(buf);
91                 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert), 
92                                        NID_countryName, buf, 100) >= 0)
93                         subject_location = g_strconcat(subject_location,", ",buf, NULL);
94         } else if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert), 
95                                        NID_countryName, buf, 100) >= 0)
96                 subject_location = g_strdup(buf);
97         else
98                 subject_location = g_strdup(_("<not in certificate>"));
99
100         if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert), 
101                                        NID_organizationName, buf, 100) >= 0)
102                 subject_organization = g_strdup(buf);
103         else 
104                 subject_organization = g_strdup(_("<not in certificate>"));
105          
106         /* fingerprint */
107         X509_digest(cert->x509_cert, EVP_md5(), md, &n);
108         fingerprint = readable_fingerprint(md, (int)n);
109
110         /* signature */
111         sig_status = ssl_certificate_check_signer(cert->x509_cert);
112
113         if (sig_status==NULL)
114                 sig_status = g_strdup(_("correct"));
115
116         vbox = gtk_vbox_new(FALSE, 5);
117         hbox = gtk_hbox_new(FALSE, 5);
118         
119         frame_owner  = gtk_frame_new(_("Owner"));
120         frame_signer = gtk_frame_new(_("Signer"));
121         frame_status = gtk_frame_new(_("Status"));
122         
123         owner_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
124         signer_table = GTK_TABLE(gtk_table_new(3, 2, FALSE));
125         status_table = GTK_TABLE(gtk_table_new(2, 2, FALSE));
126         
127         label = gtk_label_new(_("Name: "));
128         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
129         gtk_table_attach(owner_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
130         label = gtk_label_new(subject_commonname);
131         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
132         gtk_table_attach(owner_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
133         
134         label = gtk_label_new(_("Organization: "));
135         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
136         gtk_table_attach(owner_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
137         label = gtk_label_new(subject_organization);
138         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
139         gtk_table_attach(owner_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
140         
141         label = gtk_label_new(_("Location: "));
142         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
143         gtk_table_attach(owner_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
144         label = gtk_label_new(subject_location);
145         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
146         gtk_table_attach(owner_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
147
148         label = gtk_label_new(_("Name: "));
149         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
150         gtk_table_attach(signer_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
151         label = gtk_label_new(issuer_commonname);
152         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
153         gtk_table_attach(signer_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
154         
155         label = gtk_label_new(_("Organization: "));
156         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
157         gtk_table_attach(signer_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
158         label = gtk_label_new(issuer_organization);
159         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
160         gtk_table_attach(signer_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
161         
162         label = gtk_label_new(_("Location: "));
163         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
164         gtk_table_attach(signer_table, label, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
165         label = gtk_label_new(issuer_location);
166         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
167         gtk_table_attach(signer_table, label, 1, 2, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0);
168
169         label = gtk_label_new(_("Fingerprint: "));
170         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
171         gtk_table_attach(status_table, label, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
172         label = gtk_label_new(fingerprint);
173         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
174         gtk_table_attach(status_table, label, 1, 2, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
175         label = gtk_label_new(_("Signature status: "));
176         gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
177         gtk_table_attach(status_table, label, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
178         label = gtk_label_new(sig_status);
179         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
180         gtk_table_attach(status_table, label, 1, 2, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0);
181         
182         gtk_container_add(GTK_CONTAINER(frame_owner), GTK_WIDGET(owner_table));
183         gtk_container_add(GTK_CONTAINER(frame_signer), GTK_WIDGET(signer_table));
184         gtk_container_add(GTK_CONTAINER(frame_status), GTK_WIDGET(status_table));
185         
186         gtk_box_pack_end(GTK_BOX(hbox), frame_signer, TRUE, TRUE, 0);
187         gtk_box_pack_end(GTK_BOX(hbox), frame_owner, TRUE, TRUE, 0);
188         gtk_box_pack_end(GTK_BOX(vbox), frame_status, TRUE, TRUE, 0);
189         gtk_box_pack_end(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
190         
191         gtk_widget_show_all(vbox);
192         
193         g_free(issuer_commonname);
194         g_free(issuer_location);
195         g_free(issuer_organization);
196         g_free(subject_commonname);
197         g_free(subject_location);
198         g_free(subject_organization);
199         g_free(fingerprint);
200         g_free(sig_status);
201         
202         return vbox;
203 }
204
205 static gboolean sslcert_ask_hook(gpointer source, gpointer data)
206 {
207         SSLCertHookData *hookdata = (SSLCertHookData *)source;
208         if (hookdata == NULL) {
209                 return FALSE;
210         }
211         if (hookdata->old_cert == NULL)
212                 hookdata->accept = sslcertwindow_ask_new_cert(hookdata->cert);
213         else
214                 hookdata->accept = sslcertwindow_ask_changed_cert(hookdata->old_cert, hookdata->cert);
215
216         return TRUE;
217 }
218
219 void sslcertwindow_register_hook(void)
220 {
221         hooks_register_hook(SSLCERT_ASK_HOOKLIST, sslcert_ask_hook, NULL);
222 }
223
224 void sslcertwindow_show_cert(SSLCertificate *cert)
225 {
226         GtkWidget *cert_widget = cert_presenter(cert);
227         gchar *buf;
228         
229         buf = g_strdup_printf(_("SSL certificate for %s"), cert->host);
230         alertpanel_with_widget(buf, NULL, _("OK"), NULL, NULL, cert_widget);
231         g_free(buf);
232 }
233
234 static void toggle_cert_cb(GtkWidget    *widget,
235                          gpointer        data)
236 {
237         GtkWidget *cert_widget = GTK_WIDGET(data);
238         GtkWidget *box = widget->parent;
239         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
240                 if(cert_widget->parent == NULL) {
241                         gtk_box_pack_start(GTK_BOX(box), cert_widget, TRUE, TRUE, 0);
242                         gtk_widget_show(cert_widget);
243                 } else
244                         gtk_widget_show(cert_widget);
245         } else
246                 gtk_widget_hide(cert_widget);
247 }
248
249 gboolean sslcertwindow_ask_new_cert(SSLCertificate *cert)
250 {
251         gchar *buf, *sig_status;
252         AlertValue val;
253         GtkWidget *vbox;
254         GtkWidget *label;
255         GtkWidget *button;
256         GtkWidget *cert_widget;
257         
258         vbox = gtk_vbox_new(FALSE, 5);
259         buf = g_strdup_printf(_("Certificate for %s is unknown. Do you want to accept it?"), cert->host);
260         label = gtk_label_new(buf);
261         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
262         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
263         g_free(buf);
264         
265         sig_status = ssl_certificate_check_signer(cert->x509_cert);
266
267         if (sig_status==NULL)
268                 sig_status = g_strdup(_("correct"));
269
270         buf = g_strdup_printf(_("Signature status: %s"), sig_status);
271         label = gtk_label_new(buf);
272         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
273         gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
274         g_free(buf);
275         g_free(sig_status);
276         
277         button = gtk_toggle_button_new_with_label(_("View certificate"));
278         gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
279         cert_widget = cert_presenter(cert);
280         gtk_signal_connect(GTK_OBJECT(button), "toggled",
281                            GTK_SIGNAL_FUNC(toggle_cert_cb), cert_widget);
282
283         val = alertpanel_with_widget(_("Unknown SSL Certificate"), NULL, _("Accept and save"), _("Cancel connection"), NULL, vbox);
284         
285         return (val == G_ALERTDEFAULT);
286 }
287
288 gboolean sslcertwindow_ask_changed_cert(SSLCertificate *old_cert, SSLCertificate *new_cert)
289 {
290         GtkWidget *old_cert_widget = cert_presenter(old_cert);
291         GtkWidget *new_cert_widget = cert_presenter(new_cert);
292         GtkWidget *vbox;
293         gchar *buf, *sig_status;
294         GtkWidget *vbox2;
295         GtkWidget *label;
296         GtkWidget *button;
297         AlertValue val;
298         
299         vbox = gtk_vbox_new(FALSE, 5);
300         label = gtk_label_new(_("New certificate:"));
301         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
302         gtk_box_pack_end(GTK_BOX(vbox), new_cert_widget, TRUE, TRUE, 0);
303         gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
304         gtk_box_pack_end(GTK_BOX(vbox), gtk_hseparator_new(), TRUE, TRUE, 0);
305         label = gtk_label_new(_("Known certificate:"));
306         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
307         gtk_box_pack_end(GTK_BOX(vbox), old_cert_widget, TRUE, TRUE, 0);
308         gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
309         gtk_widget_show_all(vbox);
310         
311         vbox2 = gtk_vbox_new(FALSE, 5);
312         buf = g_strdup_printf(_("Certificate for %s has changed. Do you want to accept it?"), new_cert->host);
313         label = gtk_label_new(buf);
314         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
315         gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
316         g_free(buf);
317         
318         sig_status = ssl_certificate_check_signer(new_cert->x509_cert);
319
320         if (sig_status==NULL)
321                 sig_status = g_strdup(_("correct"));
322
323         buf = g_strdup_printf(_("Signature status: %s"), sig_status);
324         label = gtk_label_new(buf);
325         gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
326         gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
327         g_free(buf);
328         g_free(sig_status);
329         
330         button = gtk_toggle_button_new_with_label(_("View certificates"));
331         gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 0);
332         gtk_signal_connect(GTK_OBJECT(button), "toggled",
333                            GTK_SIGNAL_FUNC(toggle_cert_cb), vbox);
334
335         val = alertpanel_with_widget(_("Changed SSL Certificate"), NULL, _("Accept and save"), _("Cancel connection"), NULL, vbox2);
336         
337         return (val == G_ALERTDEFAULT);
338 }
339 #endif