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"
35 static char *ssl_certificate_check_signer (X509 *cert);
36 static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup);
38 static char * get_fqdn(char *host)
42 if (host == NULL || strlen(host) == 0)
45 hp = my_gethostbyname(host);
47 return g_strdup(host); /*caller should free*/
49 return g_strdup(hp->h_name);
52 static char * readable_fingerprint(unsigned char *src, int len)
63 tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
65 tmp2 = g_strdup_printf("%02X", src[i]);
74 SSLCertificate *ssl_certificate_new(X509 *x509_cert, gchar *host, gushort port)
76 return ssl_certificate_new_lookup(x509_cert, host, port, TRUE);
79 static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup)
81 SSLCertificate *cert = g_new0(SSLCertificate, 1);
83 if (host == NULL || x509_cert == NULL) {
84 ssl_certificate_destroy(cert);
87 cert->x509_cert = X509_dup(x509_cert);
89 cert->host = get_fqdn(host);
91 cert->host = g_strdup(host);
96 static void ssl_certificate_save (SSLCertificate *cert)
101 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
102 "certs", G_DIR_SEPARATOR_S, NULL);
104 if (!is_dir_exist(file))
108 port = g_strdup_printf("%d", cert->port);
109 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
110 "certs", G_DIR_SEPARATOR_S,
111 cert->host, ".", port, ".cert", NULL);
114 fp = fopen(file, "wb");
117 alertpanel_error(_("Can't save certificate !"));
120 i2d_X509_fp(fp, cert->x509_cert);
126 char* ssl_certificate_to_string(SSLCertificate *cert)
129 char *issuer_commonname, *issuer_location, *issuer_organization;
130 char *subject_commonname, *subject_location, *subject_organization;
131 char *fingerprint, *sig_status;
133 unsigned char md[EVP_MAX_MD_SIZE];
136 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
137 NID_commonName, buf, 100) >= 0)
138 issuer_commonname = g_strdup(buf);
140 issuer_commonname = g_strdup(_("<not in certificate>"));
141 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
142 NID_localityName, buf, 100) >= 0) {
143 issuer_location = g_strdup(buf);
144 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
145 NID_countryName, buf, 100) >= 0)
146 issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
147 } else if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
148 NID_countryName, buf, 100) >= 0)
149 issuer_location = g_strdup(buf);
151 issuer_location = g_strdup(_("<not in certificate>"));
153 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
154 NID_organizationName, buf, 100) >= 0)
155 issuer_organization = g_strdup(buf);
157 issuer_organization = g_strdup(_("<not in certificate>"));
160 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
161 NID_commonName, buf, 100) >= 0)
162 subject_commonname = g_strdup(buf);
164 subject_commonname = g_strdup(_("<not in certificate>"));
165 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
166 NID_localityName, buf, 100) >= 0) {
167 subject_location = g_strdup(buf);
168 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
169 NID_countryName, buf, 100) >= 0)
170 subject_location = g_strconcat(subject_location,", ",buf, NULL);
171 } else if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
172 NID_countryName, buf, 100) >= 0)
173 subject_location = g_strdup(buf);
175 subject_location = g_strdup(_("<not in certificate>"));
177 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
178 NID_organizationName, buf, 100) >= 0)
179 subject_organization = g_strdup(buf);
181 subject_organization = g_strdup(_("<not in certificate>"));
184 X509_digest(cert->x509_cert, EVP_md5(), md, &n);
185 fingerprint = readable_fingerprint(md, (int)n);
188 sig_status = ssl_certificate_check_signer(cert->x509_cert);
190 ret = g_strdup_printf(_(" Owner: %s (%s) in %s\n Signed by: %s (%s) in %s\n Fingerprint: %s\n Signature status: %s"),
191 subject_commonname, subject_organization, subject_location,
192 issuer_commonname, issuer_organization, issuer_location,
194 (sig_status==NULL ? "correct":sig_status));
196 if (issuer_commonname)
197 g_free(issuer_commonname);
199 g_free(issuer_location);
200 if (issuer_organization)
201 g_free(issuer_organization);
202 if (subject_commonname)
203 g_free(subject_commonname);
204 if (subject_location)
205 g_free(subject_location);
206 if (subject_organization)
207 g_free(subject_organization);
215 void ssl_certificate_destroy(SSLCertificate *cert)
221 X509_free(cert->x509_cert);
228 void ssl_certificate_delete_from_disk(SSLCertificate *cert)
232 buf = g_strdup_printf("%d", cert->port);
233 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
234 "certs", G_DIR_SEPARATOR_S,
235 cert->host, ".", buf, ".cert", NULL);
241 SSLCertificate *ssl_certificate_find (gchar *host, gushort port)
243 return ssl_certificate_find_lookup (host, port, TRUE);
246 SSLCertificate *ssl_certificate_find_lookup (gchar *host, gushort port, gboolean lookup)
251 SSLCertificate *cert = NULL;
256 fqdn_host = get_fqdn(host);
258 fqdn_host = g_strdup(host);
260 buf = g_strdup_printf("%d", port);
261 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
262 "certs", G_DIR_SEPARATOR_S,
263 fqdn_host, ".", buf, ".cert", NULL);
266 fp = fopen(file, "rb");
274 if ((tmp_x509 = d2i_X509_fp(fp, 0)) != NULL) {
275 cert = ssl_certificate_new_lookup(tmp_x509, fqdn_host, port, lookup);
285 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
287 if (cert_a == NULL || cert_b == NULL)
289 else if (!X509_cmp(cert_a->x509_cert, cert_b->x509_cert))
295 static char *ssl_certificate_check_signer (X509 *cert)
297 X509_STORE_CTX store_ctx;
300 char *cert_file = NULL;
301 char *err_msg = NULL;
303 store = X509_STORE_new();
305 printf("Can't create X509_STORE\n");
308 if (X509_STORE_set_default_paths(store))
310 if (X509_STORE_load_locations(store, cert_file, NULL))
314 X509_STORE_free (store);
315 return g_strdup(_("Can't load X509 default paths"));
318 X509_STORE_CTX_init (&store_ctx, store, cert, NULL);
319 ok = X509_verify_cert (&store_ctx);
322 err_msg = g_strdup(X509_verify_cert_error_string(
323 X509_STORE_CTX_get_error(&store_ctx)));
324 debug_print("Can't check signer: %s\n", err_msg);
325 X509_STORE_CTX_cleanup (&store_ctx);
326 X509_STORE_free (store);
330 X509_STORE_CTX_cleanup (&store_ctx);
331 X509_STORE_free (store);
335 gboolean ssl_certificate_check (X509 *x509_cert, gchar *host, gushort port)
337 SSLCertificate *current_cert = ssl_certificate_new(x509_cert, host, port);
338 SSLCertificate *known_cert;
340 if (current_cert == NULL) {
341 debug_print("Buggy certificate !\n");
345 known_cert = ssl_certificate_find (host, port);
347 if (known_cert == NULL) {
349 gchar *err_msg, *cur_cert_str, *sig_status;
351 sig_status = ssl_certificate_check_signer(x509_cert);
353 if (sig_status == NULL && !prefs_common.ssl_ask_unknown_valid) {
354 /* trust and accept silently if hostnames match */
355 char *buf; /* don't free buf ! */
356 if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509_cert),
357 NID_commonName, buf, 100) >= 0)
358 if (!strcmp(buf, current_cert->host)) {
360 ssl_certificate_save(current_cert);
361 ssl_certificate_destroy(current_cert);
368 cur_cert_str = ssl_certificate_to_string(current_cert);
370 err_msg = g_strdup_printf(_("%s presented an unknown SSL certificate:\n%s"),
373 g_free (cur_cert_str);
375 if (prefs_common.no_recv_err_panel) {
376 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
378 _("Don't popup error dialog on receive error"));
383 val = alertpanel(_("Warning"),
385 _("Accept and save"), _("Cancel connection"), NULL);
389 case G_ALERTALTERNATE:
390 ssl_certificate_destroy(current_cert);
393 ssl_certificate_save(current_cert);
394 ssl_certificate_destroy(current_cert);
398 else if (!ssl_certificate_compare (current_cert, known_cert)) {
400 gchar *err_msg, *known_cert_str, *cur_cert_str;
402 known_cert_str = ssl_certificate_to_string(known_cert);
403 cur_cert_str = ssl_certificate_to_string(current_cert);
404 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."),
408 g_free (cur_cert_str);
409 g_free (known_cert_str);
411 if (prefs_common.no_recv_err_panel) {
412 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
414 _("Don't popup error dialog on receive error"));
419 val = alertpanel(_("Warning"),
421 _("Accept and save"), _("Cancel connection"), NULL);
425 case G_ALERTALTERNATE:
426 ssl_certificate_destroy(current_cert);
427 ssl_certificate_destroy(known_cert);
430 ssl_certificate_save(current_cert);
431 ssl_certificate_destroy(current_cert);
432 ssl_certificate_destroy(known_cert);
437 ssl_certificate_destroy(current_cert);
438 ssl_certificate_destroy(known_cert);