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"
33 static void ssl_certificate_destroy(SSLCertificate *cert);
35 static char * convert_fingerprint(char *src)
43 while (src[i] != '\0') {
46 tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
48 tmp2 = g_strdup_printf("%02X", src[i]);
56 SSLCertificate *ssl_certificate_new(gchar *host, gchar *issuer, gchar *subject, gchar *md)
58 SSLCertificate *cert = g_new0(SSLCertificate, 1);
60 if (host == NULL || issuer == NULL || subject == NULL || md == NULL) {
61 ssl_certificate_destroy(cert);
65 cert->host = g_strdup(host);
66 cert->issuer = g_strdup(issuer);
67 cert->subject = g_strdup(subject);
68 cert->fingerprint = g_strdup(md);
72 static void ssl_certificate_save (SSLCertificate *cert)
76 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
77 "certs", G_DIR_SEPARATOR_S, NULL);
79 if (!is_dir_exist(file))
83 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
84 "certs", G_DIR_SEPARATOR_S,
85 cert->host, ".cert", NULL);
87 fp = fopen(file, "w");
90 alertpanel_error(_("Can't save certificate !"));
94 fputs(cert->issuer, fp);
95 fputs("\nsubject=", fp);
96 fputs(cert->subject, fp);
97 fputs("\nfingerprint=", fp);
98 fputs(cert->fingerprint, fp);
103 static char* ssl_certificate_to_string(SSLCertificate *cert)
106 ret = g_strdup_printf(" Issuer: %s\n Subject: %s\n Fingerprint: %s",
113 void ssl_certificate_destroy(SSLCertificate *cert)
115 g_return_if_fail(cert != NULL);
119 g_free(cert->issuer);
121 g_free(cert->subject);
122 if (cert->fingerprint)
123 g_free(cert->fingerprint);
128 static SSLCertificate *ssl_certificate_find (gchar *host)
131 gchar buf[1024], *subject, *issuer, *fingerprint;
132 SSLCertificate *cert;
135 file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
136 "certs", G_DIR_SEPARATOR_S,
137 host, ".cert", NULL);
139 fp = fopen(file, "r");
145 while( fgets( buf, sizeof( buf ), fp ) != NULL ) {
146 if (!strncmp(buf, "subject=", 8)) {
147 subject = g_strdup((char *)buf +8);
148 g_strdelimit(subject, "\r\n", '\0');
150 else if (!strncmp(buf, "issuer=", 7)) {
151 issuer = g_strdup((char *)buf +7);
152 g_strdelimit(issuer, "\r\n", '\0');
154 else if (!strncmp(buf, "fingerprint=", 12)) {
155 fingerprint = g_strdup((char *)buf +12);
156 g_strdelimit(fingerprint, "\r\n", '\0');
160 if (subject && issuer && fingerprint) {
161 cert = ssl_certificate_new(host, issuer, subject, fingerprint);
176 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
178 if (!strcmp(cert_a->issuer, cert_b->issuer)
179 && !strcmp(cert_a->subject, cert_b->subject)
180 && !strcmp(cert_a->fingerprint, cert_b->fingerprint))
186 static char *ssl_certificate_check_signer (X509 *cert)
188 X509_STORE_CTX store_ctx;
191 char *cert_file = NULL;
192 char *err_msg = NULL;
194 store = X509_STORE_new();
196 printf("Can't create X509_STORE\n");
199 if (X509_STORE_set_default_paths(store))
201 if (X509_STORE_load_locations(store, cert_file, NULL))
205 X509_STORE_free (store);
206 return g_strdup(_("Can't load X509 default paths"));
209 X509_STORE_CTX_init (&store_ctx, store, cert, NULL);
210 ok = X509_verify_cert (&store_ctx);
213 err_msg = g_strdup(X509_verify_cert_error_string(
214 X509_STORE_CTX_get_error(&store_ctx)));
215 debug_print("Can't check signer: %s\n", err_msg);
216 X509_STORE_CTX_cleanup (&store_ctx);
217 X509_STORE_free (store);
221 X509_STORE_CTX_cleanup (&store_ctx);
222 X509_STORE_free (store);
226 gboolean ssl_certificate_check (X509 *x509_cert, gchar *host, gchar *issuer,
227 gchar *subject, gchar *md)
229 char *readable_md = convert_fingerprint(md);
230 SSLCertificate *current_cert = ssl_certificate_new(host, issuer, subject, readable_md);
231 SSLCertificate *known_cert;
233 if (current_cert == NULL) {
234 debug_print("Buggy certificate !\n");
235 debug_print("host: %s\n", host);
236 debug_print("issuer: %s\n", issuer);
237 debug_print("subject: %s\n", subject);
238 debug_print("md: %s\n", readable_md);
247 known_cert = ssl_certificate_find (host);
249 if (known_cert == NULL) {
251 gchar *err_msg, *cur_cert_str;
252 gchar *sig_status = NULL;
254 cur_cert_str = ssl_certificate_to_string(current_cert);
256 sig_status = ssl_certificate_check_signer(x509_cert);
258 err_msg = g_strdup_printf(_("The SSL certificate presented by %s is unknown.\nPresented certificate is:\n%s\n\n%s%s"),
261 (sig_status == NULL)?"The presented certificate signature is correct.":"The presented certificate signature is not correct: ",
262 (sig_status == NULL)?"":sig_status);
263 g_free (cur_cert_str);
266 val = alertpanel(_("Warning"),
268 _("Accept and save"), _("Cancel connection"), NULL);
272 case G_ALERTALTERNATE:
273 ssl_certificate_destroy(current_cert);
276 ssl_certificate_save(current_cert);
277 ssl_certificate_destroy(current_cert);
281 else if (!ssl_certificate_compare (current_cert, known_cert)) {
283 gchar *err_msg, *known_cert_str, *cur_cert_str, *sig_status;
285 sig_status = ssl_certificate_check_signer(x509_cert);
287 known_cert_str = ssl_certificate_to_string(known_cert);
288 cur_cert_str = ssl_certificate_to_string(current_cert);
289 err_msg = g_strdup_printf(_("The SSL certificate presented by %s differs from the known one.\nKnown certificate is:\n%s\nPresented certificate is:\n%s\n\n%s%s"),
293 (sig_status == NULL)?"The presented certificate signature is correct.":"The presented certificate signature is not correct: ",
294 (sig_status == NULL)?"":sig_status);
295 g_free (cur_cert_str);
296 g_free (known_cert_str);
299 val = alertpanel(_("Warning"),
301 _("Accept and save"), _("Cancel connection"), NULL);
305 case G_ALERTALTERNATE:
306 ssl_certificate_destroy(current_cert);
307 ssl_certificate_destroy(known_cert);
310 ssl_certificate_save(current_cert);
311 ssl_certificate_destroy(current_cert);
312 ssl_certificate_destroy(known_cert);
317 ssl_certificate_destroy(current_cert);
318 ssl_certificate_destroy(known_cert);