2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4 * This file Copyright (C) 2002-2005 Colin Leroy <colin@colino.net>
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 2 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, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <openssl/ssl.h>
29 #include <glib/gi18n.h>
31 #include "ssl_certificate.h"
37 static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup);
39 static char * get_fqdn(char *host)
43 if (host == NULL || strlen(host) == 0)
46 hp = my_gethostbyname(host);
48 return g_strdup(host); /*caller should free*/
50 return g_strdup(hp->h_name);
53 char * readable_fingerprint(unsigned char *src, int len)
64 tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
66 tmp2 = g_strdup_printf("%02X", src[i]);
75 SSLCertificate *ssl_certificate_new(X509 *x509_cert, gchar *host, gushort port)
77 return ssl_certificate_new_lookup(x509_cert, host, port, TRUE);
80 static SSLCertificate *ssl_certificate_new_lookup(X509 *x509_cert, gchar *host, gushort port, gboolean lookup)
82 SSLCertificate *cert = g_new0(SSLCertificate, 1);
84 if (host == NULL || x509_cert == NULL) {
85 ssl_certificate_destroy(cert);
88 cert->x509_cert = X509_dup(x509_cert);
90 cert->host = get_fqdn(host);
92 cert->host = g_strdup(host);
97 static void ssl_certificate_save (SSLCertificate *cert)
102 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
103 "certs", G_DIR_SEPARATOR_S, NULL);
105 if (!is_dir_exist(file))
109 port = g_strdup_printf("%d", cert->port);
110 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
111 "certs", G_DIR_SEPARATOR_S,
112 cert->host, ".", port, ".cert", NULL);
115 fp = g_fopen(file, "wb");
118 debug_print("Can't save certificate !\n");
121 i2d_X509_fp(fp, cert->x509_cert);
127 char* ssl_certificate_to_string(SSLCertificate *cert)
130 char *issuer_commonname, *issuer_location, *issuer_organization;
131 char *subject_commonname, *subject_location, *subject_organization;
132 char *fingerprint, *sig_status;
134 unsigned char md[EVP_MAX_MD_SIZE];
137 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
138 NID_commonName, buf, 100) >= 0)
139 issuer_commonname = g_strdup(buf);
141 issuer_commonname = g_strdup(_("<not in certificate>"));
142 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
143 NID_localityName, buf, 100) >= 0) {
144 issuer_location = g_strdup(buf);
145 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
146 NID_countryName, buf, 100) >= 0)
147 issuer_location = g_strconcat(issuer_location,", ",buf, NULL);
148 } else if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
149 NID_countryName, buf, 100) >= 0)
150 issuer_location = g_strdup(buf);
152 issuer_location = g_strdup(_("<not in certificate>"));
154 if (X509_NAME_get_text_by_NID(X509_get_issuer_name(cert->x509_cert),
155 NID_organizationName, buf, 100) >= 0)
156 issuer_organization = g_strdup(buf);
158 issuer_organization = g_strdup(_("<not in certificate>"));
161 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
162 NID_commonName, buf, 100) >= 0)
163 subject_commonname = g_strdup(buf);
165 subject_commonname = g_strdup(_("<not in certificate>"));
166 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
167 NID_localityName, buf, 100) >= 0) {
168 subject_location = g_strdup(buf);
169 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
170 NID_countryName, buf, 100) >= 0)
171 subject_location = g_strconcat(subject_location,", ",buf, NULL);
172 } else if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
173 NID_countryName, buf, 100) >= 0)
174 subject_location = g_strdup(buf);
176 subject_location = g_strdup(_("<not in certificate>"));
178 if (X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509_cert),
179 NID_organizationName, buf, 100) >= 0)
180 subject_organization = g_strdup(buf);
182 subject_organization = g_strdup(_("<not in certificate>"));
185 X509_digest(cert->x509_cert, EVP_md5(), md, &n);
186 fingerprint = readable_fingerprint(md, (int)n);
189 sig_status = ssl_certificate_check_signer(cert->x509_cert);
191 ret = g_strdup_printf(_(" Owner: %s (%s) in %s\n Signed by: %s (%s) in %s\n Fingerprint: %s\n Signature status: %s"),
192 subject_commonname, subject_organization, subject_location,
193 issuer_commonname, issuer_organization, issuer_location,
195 (sig_status==NULL ? "correct":sig_status));
197 if (issuer_commonname)
198 g_free(issuer_commonname);
200 g_free(issuer_location);
201 if (issuer_organization)
202 g_free(issuer_organization);
203 if (subject_commonname)
204 g_free(subject_commonname);
205 if (subject_location)
206 g_free(subject_location);
207 if (subject_organization)
208 g_free(subject_organization);
216 void ssl_certificate_destroy(SSLCertificate *cert)
222 X509_free(cert->x509_cert);
229 void ssl_certificate_delete_from_disk(SSLCertificate *cert)
233 buf = g_strdup_printf("%d", cert->port);
234 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
235 "certs", G_DIR_SEPARATOR_S,
236 cert->host, ".", buf, ".cert", NULL);
242 SSLCertificate *ssl_certificate_find (gchar *host, gushort port)
244 return ssl_certificate_find_lookup (host, port, TRUE);
247 SSLCertificate *ssl_certificate_find_lookup (gchar *host, gushort port, gboolean lookup)
252 SSLCertificate *cert = NULL;
257 fqdn_host = get_fqdn(host);
259 fqdn_host = g_strdup(host);
261 buf = g_strdup_printf("%d", port);
262 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
263 "certs", G_DIR_SEPARATOR_S,
264 fqdn_host, ".", buf, ".cert", NULL);
267 fp = g_fopen(file, "rb");
275 if ((tmp_x509 = d2i_X509_fp(fp, 0)) != NULL) {
276 cert = ssl_certificate_new_lookup(tmp_x509, fqdn_host, port, lookup);
286 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
288 if (cert_a == NULL || cert_b == NULL)
290 else if (!X509_cmp(cert_a->x509_cert, cert_b->x509_cert))
296 char *ssl_certificate_check_signer (X509 *cert)
298 X509_STORE_CTX store_ctx;
300 char *err_msg = NULL;
302 store = X509_STORE_new();
304 printf("Can't create X509_STORE\n");
307 if (!X509_STORE_set_default_paths(store)) {
308 X509_STORE_free (store);
309 return g_strdup(_("Can't load X509 default paths"));
312 X509_STORE_CTX_init (&store_ctx, store, cert, NULL);
314 if(!X509_verify_cert (&store_ctx)) {
315 err_msg = g_strdup(X509_verify_cert_error_string(
316 X509_STORE_CTX_get_error(&store_ctx)));
317 debug_print("Can't check signer: %s\n", err_msg);
318 X509_STORE_CTX_cleanup (&store_ctx);
319 X509_STORE_free (store);
323 X509_STORE_CTX_cleanup (&store_ctx);
324 X509_STORE_free (store);
328 gboolean ssl_certificate_check (X509 *x509_cert, gchar *host, gushort port)
330 SSLCertificate *current_cert = ssl_certificate_new(x509_cert, host, port);
331 SSLCertificate *known_cert;
332 SSLCertHookData cert_hook_data;
334 if (current_cert == NULL) {
335 debug_print("Buggy certificate !\n");
339 known_cert = ssl_certificate_find (host, port);
341 if (known_cert == NULL) {
342 gchar *err_msg, *cur_cert_str, *sig_status;
344 sig_status = ssl_certificate_check_signer(x509_cert);
346 #if 0 /* disabled pref for now */
347 if (sig_status == NULL && !prefs_common.ssl_ask_unknown_valid) {
348 /* trust and accept silently if hostnames match */
349 char *buf; /* don't free buf ! */
350 if (X509_NAME_get_text_by_NID(X509_get_subject_name(x509_cert),
351 NID_commonName, buf, 100) >= 0)
352 if (!strcmp(buf, current_cert->host)) {
354 ssl_certificate_save(current_cert);
355 ssl_certificate_destroy(current_cert);
362 cur_cert_str = ssl_certificate_to_string(current_cert);
364 err_msg = g_strdup_printf(_("%s presented an unknown SSL certificate:\n%s"),
367 g_free (cur_cert_str);
369 #if 0 /* disabled for now */
370 if (prefs_common.no_recv_err_panel) {
371 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
373 _("Don't popup error dialog on receive error"));
378 cert_hook_data.cert = current_cert;
379 cert_hook_data.old_cert = NULL;
380 cert_hook_data.accept = FALSE;
382 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
386 if (!cert_hook_data.accept) {
387 ssl_certificate_destroy(current_cert);
390 ssl_certificate_save(current_cert);
391 ssl_certificate_destroy(current_cert);
395 else if (!ssl_certificate_compare (current_cert, known_cert)) {
396 gchar *err_msg, *known_cert_str, *cur_cert_str;
398 known_cert_str = ssl_certificate_to_string(known_cert);
399 cur_cert_str = ssl_certificate_to_string(current_cert);
400 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."),
404 g_free (cur_cert_str);
405 g_free (known_cert_str);
408 if (prefs_common.no_recv_err_panel) {
409 log_error(_("%s\n\nMail won't be retrieved on this account until you save the certificate.\n(Uncheck the \"%s\" preference).\n"),
411 _("Don't popup error dialog on receive error"));
416 cert_hook_data.cert = current_cert;
417 cert_hook_data.old_cert = known_cert;
418 cert_hook_data.accept = FALSE;
420 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
424 if (!cert_hook_data.accept) {
425 ssl_certificate_destroy(current_cert);
426 ssl_certificate_destroy(known_cert);
429 ssl_certificate_save(current_cert);
430 ssl_certificate_destroy(current_cert);
431 ssl_certificate_destroy(known_cert);
436 ssl_certificate_destroy(current_cert);
437 ssl_certificate_destroy(known_cert);
441 #endif /* USE_OPENSSL */