2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto
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.
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.
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.
26 #include <openssl/ssl.h>
28 #include "ssl_certificate.h"
29 #include "alertpanel.h"
32 #include "prefs_common.h"
34 static void ssl_certificate_destroy(SSLCertificate *cert);
35 static char *ssl_certificate_check_signer (X509 *cert);
37 static char * readable_fingerprint(unsigned char *src, int len)
48 tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
50 tmp2 = g_strdup_printf("%02X", src[i]);
59 SSLCertificate *ssl_certificate_new(X509 *x509_cert, gchar *host)
61 SSLCertificate *cert = g_new0(SSLCertificate, 1);
63 if (host == NULL || x509_cert == NULL) {
64 ssl_certificate_destroy(cert);
68 cert->x509_cert = X509_dup(x509_cert);
69 cert->host = g_strdup(host);
73 static void ssl_certificate_save (SSLCertificate *cert)
77 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
78 "certs", G_DIR_SEPARATOR_S, NULL);
80 if (!is_dir_exist(file))
84 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
85 "certs", G_DIR_SEPARATOR_S,
86 cert->host, ".cert", NULL);
88 fp = fopen(file, "w");
91 alertpanel_error(_("Can't save certificate !"));
94 i2d_X509_fp(fp, cert->x509_cert);
100 static char* ssl_certificate_to_string(SSLCertificate *cert)
103 char *issuer_commonname, *issuer_location, *issuer_organization;
104 char *subject_commonname, *subject_location, *subject_organization;
105 char *fingerprint, *sig_status;
107 unsigned char md[EVP_MAX_MD_SIZE];
110 X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
111 NID_commonName, buf, 100);
112 issuer_commonname = g_strdup(buf);
113 X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
114 NID_localityName, buf, 100);
115 issuer_location = g_strdup(buf);
116 X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
117 NID_countryName, buf, 100);
118 issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
119 X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
120 NID_organizationName, buf, 100);
121 issuer_organization = g_strdup(buf);
124 X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
125 NID_commonName, buf, 100);
126 subject_commonname = g_strdup(buf);
127 X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
128 NID_localityName, buf, 100);
129 subject_location = g_strdup(buf);
130 X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
131 NID_countryName, buf, 100);
132 subject_location = g_strconcat(subject_location,", ",buf, NULL);
133 X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
134 NID_organizationName, buf, 100);
135 subject_organization = g_strdup(buf);
138 X509_digest(cert->x509_cert, EVP_md5(), md, &n);
139 fingerprint = readable_fingerprint(md, (int)n);
142 sig_status = ssl_certificate_check_signer(cert->x509_cert);
144 ret = g_strdup_printf(_(" Owner: %s (%s) in %s\n Signed by: %s (%s) in %s\n Fingerprint: %s\n Signature status: %s"),
145 subject_commonname, subject_organization, subject_location,
146 issuer_commonname, issuer_organization, issuer_location,
148 (sig_status==NULL ? "correct":sig_status));
150 if (issuer_commonname)
151 g_free(issuer_commonname);
153 g_free(issuer_location);
154 if (issuer_organization)
155 g_free(issuer_organization);
156 if (subject_commonname)
157 g_free(subject_commonname);
158 if (subject_location)
159 g_free(subject_location);
160 if (subject_organization)
161 g_free(subject_organization);
169 void ssl_certificate_destroy(SSLCertificate *cert)
171 g_return_if_fail(cert != NULL);
173 X509_free(cert->x509_cert);
180 static SSLCertificate *ssl_certificate_find (gchar *host)
183 gchar buf[1024], *subject, *issuer, *fingerprint;
184 SSLCertificate *cert = NULL;
188 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
189 "certs", G_DIR_SEPARATOR_S,
190 host, ".cert", NULL);
192 fp = fopen(file, "r");
199 if ((tmp_x509 = d2i_X509_fp(fp, 0)) != NULL) {
200 cert = ssl_certificate_new(tmp_x509, host);
209 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
211 if (cert_a == NULL || cert_b == NULL)
213 else if (!X509_cmp(cert_a->x509_cert, cert_b->x509_cert))
219 static char *ssl_certificate_check_signer (X509 *cert)
221 X509_STORE_CTX store_ctx;
224 char *cert_file = NULL;
225 char *err_msg = NULL;
227 store = X509_STORE_new();
229 printf("Can't create X509_STORE\n");
232 if (X509_STORE_set_default_paths(store))
234 if (X509_STORE_load_locations(store, cert_file, NULL))
238 X509_STORE_free (store);
239 return g_strdup(_("Can't load X509 default paths"));
242 X509_STORE_CTX_init (&store_ctx, store, cert, NULL);
243 ok = X509_verify_cert (&store_ctx);
246 err_msg = g_strdup(X509_verify_cert_error_string(
247 X509_STORE_CTX_get_error(&store_ctx)));
248 debug_print("Can't check signer: %s\n", err_msg);
249 X509_STORE_CTX_cleanup (&store_ctx);
250 X509_STORE_free (store);
254 X509_STORE_CTX_cleanup (&store_ctx);
255 X509_STORE_free (store);
259 gboolean ssl_certificate_check (X509 *x509_cert, gchar *host)
261 SSLCertificate *current_cert = ssl_certificate_new(x509_cert, host);
262 SSLCertificate *known_cert;
264 if (current_cert == NULL) {
265 debug_print("Buggy certificate !\n");
269 known_cert = ssl_certificate_find (host);
271 if (known_cert == NULL) {
273 gchar *err_msg, *cur_cert_str;
275 cur_cert_str = ssl_certificate_to_string(current_cert);
277 err_msg = g_strdup_printf(_("%s presented an unknown SSL certificate:\n%s"),
280 g_free (cur_cert_str);
282 if (prefs_common.no_recv_err_panel) {
283 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
285 _("Don't popup error dialog on receive error"));
290 val = alertpanel(_("Warning"),
292 _("Accept and save"), _("Cancel connection"), NULL);
296 case G_ALERTALTERNATE:
297 ssl_certificate_destroy(current_cert);
300 ssl_certificate_save(current_cert);
301 ssl_certificate_destroy(current_cert);
305 else if (!ssl_certificate_compare (current_cert, known_cert)) {
307 gchar *err_msg, *known_cert_str, *cur_cert_str, *sig_status;
309 sig_status = ssl_certificate_check_signer(x509_cert);
311 known_cert_str = ssl_certificate_to_string(known_cert);
312 cur_cert_str = ssl_certificate_to_string(current_cert);
313 err_msg = g_strdup_printf(_("%s's SSL certificate changed !\nWe have saved this one:\n%s\n\nIt is now:\n%s\n\nThis could mean the server answering is not the known one."),
317 g_free (cur_cert_str);
318 g_free (known_cert_str);
322 if (prefs_common.no_recv_err_panel) {
323 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
325 _("Don't popup error dialog on receive error"));
330 val = alertpanel(_("Warning"),
332 _("Accept and save"), _("Cancel connection"), NULL);
336 case G_ALERTALTERNATE:
337 ssl_certificate_destroy(current_cert);
338 ssl_certificate_destroy(known_cert);
341 ssl_certificate_save(current_cert);
342 ssl_certificate_destroy(current_cert);
343 ssl_certificate_destroy(known_cert);
348 ssl_certificate_destroy(current_cert);
349 ssl_certificate_destroy(known_cert);