talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

ssl_certificate.c (25826B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2016 Colin Leroy and the Claws Mail team
      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 3 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, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include <gnutls/gnutls.h>
     20 #include <gnutls/x509.h>
     21 #include <gnutls/pkcs12.h>
     22 #include <sys/stat.h>
     23 #include <unistd.h>
     24 #include <string.h>
     25 #include <sys/types.h>
     26 #include <stdio.h>
     27 #include <glib.h>
     28 #include <glib/gi18n.h>
     29 #include <errno.h>
     30 #include <sys/socket.h>
     31 #include <netinet/in.h>
     32 #include "ssl_certificate.h"
     33 #include "utils.h"
     34 #include "log.h"
     35 #include "socket.h"
     36 #include "hooks.h"
     37 #include "defs.h"
     38 #include "file-utils.h"
     39 
     40 static GHashTable *warned_expired = NULL;
     41 
     42 gboolean prefs_common_unsafe_ssl_certs(void);
     43 
     44 static gchar *get_certificate_path(const gchar *host, const gchar *port, const gchar *fp)
     45 {
     46 	gchar *ret;
     47 	gchar *filename;
     48 
     49 	if (fp != NULL && prefs_common_unsafe_ssl_certs()) {
     50 		filename = g_strconcat(host, ".", port, ".", fp, ".cert", NULL);
     51 	} else {
     52 		filename = g_strconcat(host, ".", port, ".cert", NULL);
     53 	}
     54 	subst_for_filename(filename);
     55 
     56 	ret = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
     57 		  "certs", G_DIR_SEPARATOR_S,
     58 		  filename, NULL);
     59 
     60 	g_free(filename);
     61 	return ret;
     62 }
     63 
     64 static gchar *get_certificate_chain_path(const gchar *host, const gchar *port, const gchar *fp)
     65 {
     66 	gchar *tmp = get_certificate_path(host, port, fp);
     67 	gchar *result = g_strconcat(tmp, ".chain", NULL);
     68 
     69 	g_free(tmp);
     70 
     71 	return result;
     72 }
     73 
     74 char * readable_fingerprint(unsigned char *src, int len)
     75 {
     76 	int i=0;
     77 	char * ret;
     78 
     79 	if (src == NULL)
     80 		return NULL;
     81 	ret = g_strdup("");
     82 	while (i < len) {
     83 		char *tmp2;
     84 		if(i>0)
     85 			tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
     86 		else
     87 			tmp2 = g_strdup_printf("%02X", src[i]);
     88 		g_free(ret);
     89 		ret = g_strdup(tmp2);
     90 		g_free(tmp2);
     91 		i++;
     92 	}
     93 	return ret;
     94 }
     95 
     96 static gnutls_x509_crt_t x509_crt_copy(gnutls_x509_crt_t src)
     97 {
     98     int ret;
     99     size_t size;
    100     gnutls_datum_t tmp;
    101     gnutls_x509_crt_t dest;
    102     size = 0;
    103 
    104     if (gnutls_x509_crt_init(&dest) != 0) {
    105 	g_warning("couldn't gnutls_x509_crt_init");
    106         return NULL;
    107     }
    108 
    109     if (gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, NULL, &size)
    110         != GNUTLS_E_SHORT_MEMORY_BUFFER) {
    111 	g_warning("couldn't gnutls_x509_crt_export to get size");
    112         gnutls_x509_crt_deinit(dest);
    113         return NULL;
    114     }
    115 
    116     tmp.data = malloc(size);
    117     memset(tmp.data, 0, size);
    118     ret = gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, tmp.data, &size);
    119     if (ret == 0) {
    120         tmp.size = size;
    121         ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER);
    122 	if (ret) {
    123 		g_warning("couldn't gnutls_x509_crt_import for real (%d %s)", ret, gnutls_strerror(ret));
    124 		gnutls_x509_crt_deinit(dest);
    125 		dest = NULL;
    126 	}
    127     } else {
    128 	g_warning("couldn't gnutls_x509_crt_export for real (%d %s)", ret, gnutls_strerror(ret));
    129         gnutls_x509_crt_deinit(dest);
    130         dest = NULL;
    131     }
    132 
    133     free(tmp.data);
    134     return dest;
    135 }
    136 
    137 static SSLCertificate *ssl_certificate_new(gnutls_x509_crt_t x509_cert, const gchar *host, gushort port)
    138 {
    139 	SSLCertificate *cert = g_new0(SSLCertificate, 1);
    140 	size_t n;
    141 	unsigned char md[128];
    142 
    143 	if (host == NULL || x509_cert == NULL) {
    144 		ssl_certificate_destroy(cert);
    145 		return NULL;
    146 	}
    147 	cert->x509_cert = x509_crt_copy(x509_cert);
    148 	cert->status = (guint)-1;
    149 	cert->host = g_strdup(host);
    150 	cert->port = port;
    151 
    152 	/* fingerprint */
    153 	n = sizeof(md);
    154 	gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_MD5, md, &n);
    155 	cert->fingerprint = readable_fingerprint(md, (int)n);
    156 	return cert;
    157 }
    158 
    159 static void gnutls_export_X509_fp(FILE *fp, gnutls_x509_crt_t x509_cert, gnutls_x509_crt_fmt_t format)
    160 {
    161 	char output[10*1024];
    162 	size_t cert_size = 10*1024;
    163 	int r;
    164 
    165 	if ((r = gnutls_x509_crt_export(x509_cert, format, output, &cert_size)) < 0) {
    166 		g_warning("couldn't export cert %s (%"G_GSIZE_FORMAT")", gnutls_strerror(r), cert_size);
    167 		return;
    168 	}
    169 	debug_print("writing %" G_GSIZE_FORMAT " bytes\n",cert_size);
    170 	if (fwrite(&output, 1, cert_size, fp) < cert_size) {
    171 		g_warning("failed to write cert: %d %s", errno, g_strerror(errno));
    172 	}
    173 }
    174 
    175 size_t gnutls_i2d_X509(gnutls_x509_crt_t x509_cert, unsigned char **output)
    176 {
    177 	size_t cert_size = 10*1024;
    178 	int r;
    179 
    180 	if (output == NULL)
    181 		return 0;
    182 
    183 	*output = malloc(cert_size);
    184 
    185 	if ((r = gnutls_x509_crt_export(x509_cert, GNUTLS_X509_FMT_DER, *output, &cert_size)) < 0) {
    186 		g_warning("couldn't export cert %s (%"G_GSIZE_FORMAT")", gnutls_strerror(r), cert_size);
    187 		free(*output);
    188 		*output = NULL;
    189 		return 0;
    190 	}
    191 	return cert_size;
    192 }
    193 
    194 size_t gnutls_i2d_PrivateKey(gnutls_x509_privkey_t pkey, unsigned char **output)
    195 {
    196 	size_t key_size = 10*1024;
    197 	int r;
    198 
    199 	if (output == NULL)
    200 		return 0;
    201 
    202 	*output = malloc(key_size);
    203 
    204 	if ((r = gnutls_x509_privkey_export(pkey, GNUTLS_X509_FMT_DER, *output, &key_size)) < 0) {
    205 		g_warning("couldn't export key %s (%"G_GSIZE_FORMAT")", gnutls_strerror(r), key_size);
    206 		free(*output);
    207 		*output = NULL;
    208 		return 0;
    209 	}
    210 	return key_size;
    211 }
    212 
    213 static int gnutls_import_X509_list_fp(FILE *fp, gnutls_x509_crt_fmt_t format,
    214 				   gnutls_x509_crt_t **cert_list, gint *num_certs)
    215 {
    216 	gnutls_x509_crt_t *crt_list;
    217 	unsigned int max = 512;
    218 	unsigned int flags = 0;
    219 	gnutls_datum_t tmp;
    220 	struct stat s;
    221 	int r;
    222 
    223 	*cert_list = NULL;
    224 	*num_certs = 0;
    225 
    226 	if (fp == NULL)
    227 		return -ENOENT;
    228 
    229 	if (fstat(fileno(fp), &s) < 0) {
    230 		perror("fstat");
    231 		return -errno;
    232 	}
    233 
    234 	crt_list=(gnutls_x509_crt_t*)malloc(max*sizeof(gnutls_x509_crt_t));
    235 	tmp.data = malloc(s.st_size);
    236 	memset(tmp.data, 0, s.st_size);
    237 	tmp.size = s.st_size;
    238 	if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
    239 		perror("fread");
    240 		free(tmp.data);
    241 		free(crt_list);
    242 		return -EIO;
    243 	}
    244 
    245 	if ((r = gnutls_x509_crt_list_import(crt_list, &max,
    246 			&tmp, format, flags)) < 0) {
    247 		debug_print("cert import failed: %s\n", gnutls_strerror(r));
    248 		free(tmp.data);
    249 		free(crt_list);
    250 		return r;
    251 	}
    252 	free(tmp.data);
    253 	debug_print("got %d certs in crt_list! %p\n", max, &crt_list);
    254 
    255 	*cert_list = crt_list;
    256 	*num_certs = max;
    257 
    258 	return r;
    259 }
    260 
    261 /* return one certificate, read from file */
    262 static gnutls_x509_crt_t gnutls_import_X509_fp(FILE *fp, gnutls_x509_crt_fmt_t format)
    263 {
    264 	gnutls_x509_crt_t *certs = NULL;
    265 	gnutls_x509_crt_t cert = NULL;
    266 	int i, ncerts, r;
    267 
    268 	if ((r = gnutls_import_X509_list_fp(fp, format, &certs, &ncerts)) < 0) {
    269 		return NULL;
    270 	}
    271 
    272 	if (ncerts == 0)
    273 		return NULL;
    274 
    275 	for (i = 1; i < ncerts; i++)
    276 		gnutls_x509_crt_deinit(certs[i]);
    277 
    278 	cert = certs[0];
    279 	free(certs);
    280 
    281 	return cert;
    282 }
    283 
    284 static gnutls_x509_privkey_t gnutls_import_key_fp(FILE *fp, gnutls_x509_crt_fmt_t format)
    285 {
    286 	gnutls_x509_privkey_t key = NULL;
    287 	gnutls_datum_t tmp;
    288 	struct stat s;
    289 	int r;
    290 	if (fstat(fileno(fp), &s) < 0) {
    291 		perror("fstat");
    292 		return NULL;
    293 	}
    294 	tmp.data = malloc(s.st_size);
    295 	memset(tmp.data, 0, s.st_size);
    296 	tmp.size = s.st_size;
    297 	if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
    298 		perror("fread");
    299 		free(tmp.data);
    300 		return NULL;
    301 	}
    302 
    303 	gnutls_x509_privkey_init(&key);
    304 	if ((r = gnutls_x509_privkey_import(key, &tmp, format)) < 0) {
    305 		debug_print("key import failed: %s\n", gnutls_strerror(r));
    306 		gnutls_x509_privkey_deinit(key);
    307 		key = NULL;
    308 	}
    309 	free(tmp.data);
    310 	debug_print("got key! %p\n", key);
    311 	return key;
    312 }
    313 
    314 static gnutls_pkcs12_t gnutls_import_PKCS12_fp(FILE *fp, gnutls_x509_crt_fmt_t format)
    315 {
    316 	gnutls_pkcs12_t p12 = NULL;
    317 	gnutls_datum_t tmp;
    318 	struct stat s;
    319 	int r;
    320 	if (fstat(fileno(fp), &s) < 0) {
    321 		log_error(LOG_PROTOCOL, _("Cannot stat P12 certificate file (%s)\n"),
    322 				  g_strerror(errno));
    323 		return NULL;
    324 	}
    325 	tmp.data = malloc(s.st_size);
    326 	memset(tmp.data, 0, s.st_size);
    327 	tmp.size = s.st_size;
    328 	if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
    329 		log_error(LOG_PROTOCOL, _("Cannot read P12 certificate file (%s)\n"),
    330 				  g_strerror(errno));
    331 		free(tmp.data);
    332 		return NULL;
    333 	}
    334 
    335 	gnutls_pkcs12_init(&p12);
    336 
    337 	if ((r = gnutls_pkcs12_import(p12, &tmp, format, 0)) < 0) {
    338 		log_error(LOG_PROTOCOL, _("Cannot import P12 certificate file (%s)\n"),
    339 				  gnutls_strerror(r));
    340 		gnutls_pkcs12_deinit(p12);
    341 		p12 = NULL;
    342 	}
    343 	free(tmp.data);
    344 	debug_print("got p12! %p\n", p12);
    345 	return p12;
    346 }
    347 
    348 static void ssl_certificate_save (SSLCertificate *cert)
    349 {
    350 	gchar *file, *port;
    351 	FILE *fp;
    352 
    353 	file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
    354 			  "certs", G_DIR_SEPARATOR_S, NULL);
    355 
    356 	if (!is_dir_exist(file))
    357 		make_dir_hier(file);
    358 	g_free(file);
    359 
    360 	port = g_strdup_printf("%d", cert->port);
    361 	file = get_certificate_path(cert->host, port, cert->fingerprint);
    362 
    363 	g_free(port);
    364 	fp = g_fopen(file, "wb");
    365 	if (fp == NULL) {
    366 		g_free(file);
    367 		debug_print("Can't save certificate !\n");
    368 		return;
    369 	}
    370 
    371 	gnutls_export_X509_fp(fp, cert->x509_cert, GNUTLS_X509_FMT_DER);
    372 
    373 	g_free(file);
    374 	fclose(fp);
    375 
    376 }
    377 
    378 void ssl_certificate_destroy(SSLCertificate *cert)
    379 {
    380 	if (cert == NULL)
    381 		return;
    382 
    383 	if (cert->x509_cert)
    384 		gnutls_x509_crt_deinit(cert->x509_cert);
    385 	g_free(cert->host);
    386 	g_free(cert->fingerprint);
    387 	g_free(cert);
    388 	cert = NULL;
    389 }
    390 
    391 void ssl_certificate_delete_from_disk(SSLCertificate *cert)
    392 {
    393 	gchar *buf;
    394 	gchar *file;
    395 	buf = g_strdup_printf("%d", cert->port);
    396 	file = get_certificate_path(cert->host, buf, cert->fingerprint);
    397 	unlink (file);
    398 	g_free(file);
    399 	file = get_certificate_chain_path(cert->host, buf, cert->fingerprint);
    400 	unlink (file);
    401 	g_free(file);
    402 	g_free(buf);
    403 }
    404 
    405 SSLCertificate *ssl_certificate_find (const gchar *host, gushort port, const gchar *fingerprint)
    406 {
    407 	gchar *file = NULL;
    408 	gchar *buf;
    409 	SSLCertificate *cert = NULL;
    410 	gnutls_x509_crt_t tmp_x509;
    411 	FILE *fp = NULL;
    412 	gboolean must_rename = FALSE;
    413 
    414 	buf = g_strdup_printf("%d", port);
    415 
    416 	if (fingerprint != NULL) {
    417 		file = get_certificate_path(host, buf, fingerprint);
    418 		fp = g_fopen(file, "rb");
    419 	}
    420 	if (fp == NULL) {
    421 		/* see if we have the old one */
    422 		debug_print("didn't get %s\n", file);
    423 		g_free(file);
    424 		file = get_certificate_path(host, buf, NULL);
    425 		fp = g_fopen(file, "rb");
    426 
    427 		if (fp) {
    428 			debug_print("got %s\n", file);
    429 			must_rename = (fingerprint != NULL);
    430 		}
    431 	} else {
    432 		debug_print("got %s first try\n", file);
    433 	}
    434 	if (fp == NULL) {
    435 		g_free(file);
    436 		g_free(buf);
    437 		return NULL;
    438 	}
    439 
    440 	if ((tmp_x509 = gnutls_import_X509_fp(fp, GNUTLS_X509_FMT_DER)) != NULL) {
    441 		cert = ssl_certificate_new(tmp_x509, host, port);
    442 		debug_print("got cert %p\n", cert);
    443 		gnutls_x509_crt_deinit(tmp_x509);
    444 	}
    445 
    446 	fclose(fp);
    447 	g_free(file);
    448 
    449 	if (must_rename) {
    450 		gchar *old = get_certificate_path(host, buf, NULL);
    451 		gchar *new = get_certificate_path(host, buf, fingerprint);
    452 		if (strcmp(old, new))
    453 			rename(old, new);
    454 		g_free(old);
    455 		g_free(new);
    456 	}
    457 	g_free(buf);
    458 
    459 	return cert;
    460 }
    461 
    462 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
    463 {
    464 	char *output_a;
    465 	char *output_b;
    466 	size_t cert_size_a = 0, cert_size_b = 0;
    467 	int r;
    468 
    469 	if (cert_a == NULL || cert_b == NULL)
    470 		return FALSE;
    471 
    472 	if ((r = gnutls_x509_crt_export(cert_a->x509_cert, GNUTLS_X509_FMT_DER, NULL, &cert_size_a))
    473 	    != GNUTLS_E_SHORT_MEMORY_BUFFER) {
    474 		g_warning("couldn't gnutls_x509_crt_export to get size a %s", gnutls_strerror(r));
    475 		return FALSE;
    476 	}
    477 
    478 	if ((r = gnutls_x509_crt_export(cert_b->x509_cert, GNUTLS_X509_FMT_DER, NULL, &cert_size_b))
    479 	    != GNUTLS_E_SHORT_MEMORY_BUFFER) {
    480 		g_warning("couldn't gnutls_x509_crt_export to get size b %s", gnutls_strerror(r));
    481 		return FALSE;
    482 	}
    483 
    484 	if (cert_size_a != cert_size_b) {
    485 		debug_print("sizes differ: %"G_GSIZE_FORMAT" != %"G_GSIZE_FORMAT"\n", cert_size_a, cert_size_b);
    486 		return FALSE;
    487 	}
    488 
    489 	output_a = g_malloc(cert_size_a);
    490 	output_b = g_malloc(cert_size_b);
    491 	if ((r = gnutls_x509_crt_export(cert_a->x509_cert, GNUTLS_X509_FMT_DER, output_a, &cert_size_a)) < 0) {
    492 		g_warning("couldn't gnutls_x509_crt_export a %s", gnutls_strerror(r));
    493 		g_free(output_a);
    494 		g_free(output_b);
    495 		return FALSE;
    496 	}
    497 	if ((r = gnutls_x509_crt_export(cert_b->x509_cert, GNUTLS_X509_FMT_DER, output_b, &cert_size_b)) < 0) {
    498 		g_warning("couldn't gnutls_x509_crt_export b %s", gnutls_strerror(r));
    499 		g_free(output_a);
    500 		g_free(output_b);
    501 		return FALSE;
    502 	}
    503 	if (memcmp(output_a, output_b, cert_size_a)) {
    504 		debug_print("contents differ\n");
    505 		g_free(output_a);
    506 		g_free(output_b);
    507 		return FALSE;
    508 	}
    509 	g_free(output_a);
    510 	g_free(output_b);
    511 
    512 	return TRUE;
    513 }
    514 
    515 static guint check_cert(SSLCertificate *cert)
    516 {
    517 	gnutls_x509_crt_t *ca_list = NULL;
    518 	gnutls_x509_crt_t *chain = NULL;
    519 	unsigned int max_ca = 512, max_certs;
    520 	unsigned int flags = 0;
    521 	int r, i;
    522 	unsigned int status;
    523 	gchar *chain_file = NULL, *buf = NULL;
    524 	FILE *fp;
    525 
    526 	if (claws_ssl_get_cert_file())
    527 		fp = g_fopen(claws_ssl_get_cert_file(), "r");
    528 	else
    529 		return (guint)-1;
    530 
    531 	if (fp == NULL)
    532 		return (guint)-1;
    533 
    534 	if ((r = gnutls_import_X509_list_fp(fp, GNUTLS_X509_FMT_PEM, &ca_list, &max_ca)) < 0) {
    535 		debug_print("CA import failed: %s\n", gnutls_strerror(r));
    536 		fclose(fp);
    537 		return (guint)-1;
    538 	}
    539 	fclose(fp);
    540 	fp = NULL;
    541 
    542 	buf = g_strdup_printf("%d", cert->port);
    543 	chain_file = get_certificate_chain_path(cert->host, buf, cert->fingerprint);
    544 	g_free(buf);
    545 	if (is_file_exist(chain_file)) {
    546 		unsigned char md[128];
    547 		size_t n = 128;
    548 		char *fingerprint;
    549 
    550 		fp = g_fopen(chain_file, "r");
    551 		if (fp == NULL) {
    552 			debug_print("g_fopen %s failed: %s\n", chain_file, g_strerror(errno));
    553 			g_free(chain_file);
    554 			return (guint)-1;
    555 		}
    556 		if ((r = gnutls_import_X509_list_fp(fp, GNUTLS_X509_FMT_PEM, &chain, &max_certs)) < 0) {
    557 			debug_print("chain import failed: %s\n", gnutls_strerror(r));
    558 			fclose(fp);
    559 			g_free(chain_file);
    560 			return (guint)-1;
    561 		}
    562 		g_free(chain_file);
    563 		fclose(fp);
    564 		fp = NULL;
    565 
    566 		gnutls_x509_crt_get_fingerprint(chain[0], GNUTLS_DIG_MD5, md, &n);
    567 		fingerprint = readable_fingerprint(md, n);
    568 		if (!fingerprint || strcmp(fingerprint, cert->fingerprint)) {
    569 			debug_print("saved chain fingerprint does not match current : %s / %s\n",
    570 				cert->fingerprint, fingerprint);
    571 
    572 			return (guint)-1;
    573 		}
    574 		g_free(fingerprint);
    575 
    576 		r = gnutls_x509_crt_list_verify (chain,
    577 				     max_certs,
    578 				     ca_list, max_ca,
    579 				     NULL, 0,
    580 				     GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
    581 				     &status);
    582 		if (r < 0)
    583 			debug_print("chain check failed: %s\n", gnutls_strerror(r));
    584 
    585 		for (i = 0; i < max_certs; i++)
    586 			gnutls_x509_crt_deinit(chain[i]);
    587 		free(chain);
    588 
    589 	} else {
    590 		r = gnutls_x509_crt_verify(cert->x509_cert, ca_list, max_ca, flags, &status);
    591 		if (r < 0)
    592 			debug_print("cert check failed: %s\n", gnutls_strerror(r));
    593 	}
    594 
    595 	for (i = 0; i < max_ca; i++)
    596 		gnutls_x509_crt_deinit(ca_list[i]);
    597 	free(ca_list);
    598 
    599 	if (r < 0)
    600 		return (guint)-1;
    601 	else
    602 		return status;
    603 
    604 }
    605 
    606 static gboolean ssl_certificate_is_valid(SSLCertificate *cert, guint status)
    607 {
    608 	gchar *str_status = ssl_certificate_check_signer(cert, status);
    609 
    610 	if (str_status != NULL) {
    611 		g_free(str_status);
    612 		return FALSE;
    613 	}
    614 	return ssl_certificate_check_subject_cn(cert);
    615 }
    616 
    617 char *ssl_certificate_check_signer (SSLCertificate *cert, guint status)
    618 {
    619 	gnutls_x509_crt_t x509_cert = cert ? cert->x509_cert : NULL;
    620 
    621 	if (!cert)
    622 		return g_strdup(_("Internal error"));
    623 
    624 	if (status == (guint)-1) {
    625 		status = check_cert(cert);
    626 		if (status == -1)
    627 			return g_strdup(_("Uncheckable"));
    628 	}
    629 	if (status & GNUTLS_CERT_INVALID) {
    630 		if (gnutls_x509_crt_check_issuer(x509_cert, x509_cert))
    631 			return g_strdup(_("Self-signed certificate"));
    632 	}
    633 	if (status & GNUTLS_CERT_REVOKED)
    634 		return g_strdup(_("Revoked certificate"));
    635 	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
    636 		return g_strdup(_("No certificate issuer found"));
    637 	if (status & GNUTLS_CERT_SIGNER_NOT_CA)
    638 		return g_strdup(_("Certificate issuer is not a CA"));
    639 
    640 
    641 	return NULL;
    642 }
    643 
    644 static void ssl_certificate_save_chain(gnutls_x509_crt_t *certs, gint len, const gchar *host, gushort port)
    645 {
    646 	gint i;
    647 	gchar *file = NULL;
    648 	FILE *fp = NULL;
    649 
    650 	for (i = 0; i < len; i++) {
    651 		size_t n;
    652 		unsigned char md[128];
    653 		gnutls_x509_crt_t cert = certs[i];
    654 
    655 		if (i == 0) {
    656 			n = sizeof(md);
    657 			gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_MD5, md, &n);
    658 			gchar *fingerprint = readable_fingerprint(md, n);
    659 			gchar *buf = g_strdup_printf("%d", port);
    660 
    661 			file = get_certificate_chain_path(host, buf, fingerprint);
    662 			g_free(fingerprint);
    663 
    664 			g_free(buf);
    665 
    666 			fp = g_fopen(file, "wb");
    667 			if (fp == NULL) {
    668 				g_free(file);
    669 				debug_print("Can't save certificate !\n");
    670 				return;
    671 			}
    672 			g_free(file);
    673 		}
    674 
    675 		gnutls_export_X509_fp(fp, cert, GNUTLS_X509_FMT_PEM);
    676 
    677 	}
    678 	if (fp)
    679 		fclose(fp);
    680 }
    681 
    682 gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status,
    683 				const gchar *host, gushort port,
    684 				gboolean accept_if_valid)
    685 {
    686 	SSLCertificate *current_cert = NULL;
    687 	SSLCertificate *known_cert;
    688 	SSLCertHookData cert_hook_data;
    689 	gchar *fingerprint;
    690 	size_t n;
    691 	unsigned char md[128];
    692 	gboolean valid = FALSE;
    693 
    694 	current_cert = ssl_certificate_new(x509_cert, host, port);
    695 
    696 	if (current_cert == NULL) {
    697 		debug_print("Buggy certificate !\n");
    698 		return FALSE;
    699 	}
    700 
    701 	current_cert->status = status;
    702 	/* fingerprint */
    703 	n = sizeof(md);
    704 	gnutls_x509_crt_get_fingerprint(x509_cert, GNUTLS_DIG_MD5, md, &n);
    705 	fingerprint = readable_fingerprint(md, n);
    706 
    707 	known_cert = ssl_certificate_find(host, port, fingerprint);
    708 
    709 	g_free(fingerprint);
    710 
    711 	if (accept_if_valid)
    712 		valid = ssl_certificate_is_valid(current_cert, status);
    713 	else
    714 		valid = FALSE; /* Force check */
    715 
    716 	if (known_cert == NULL) {
    717 		if (valid) {
    718 			ssl_certificate_save(current_cert);
    719 			ssl_certificate_destroy(current_cert);
    720 			return TRUE;
    721 		}
    722 
    723 		cert_hook_data.cert = current_cert;
    724 		cert_hook_data.old_cert = NULL;
    725 		cert_hook_data.expired = FALSE;
    726 		cert_hook_data.accept = FALSE;
    727 
    728 		hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
    729 
    730 		if (!cert_hook_data.accept) {
    731 			ssl_certificate_destroy(current_cert);
    732 			return FALSE;
    733 		} else {
    734 			ssl_certificate_save(current_cert);
    735 			ssl_certificate_destroy(current_cert);
    736 			return TRUE;
    737 		}
    738 	} else if (!ssl_certificate_compare (current_cert, known_cert)) {
    739 		if (valid) {
    740 			ssl_certificate_save(current_cert);
    741 			ssl_certificate_destroy(current_cert);
    742 			ssl_certificate_destroy(known_cert);
    743 			return TRUE;
    744 		}
    745 
    746 		cert_hook_data.cert = current_cert;
    747 		cert_hook_data.old_cert = known_cert;
    748 		cert_hook_data.expired = FALSE;
    749 		cert_hook_data.accept = FALSE;
    750 
    751 		hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
    752 
    753 		if (!cert_hook_data.accept) {
    754 			ssl_certificate_destroy(current_cert);
    755 			ssl_certificate_destroy(known_cert);
    756 			return FALSE;
    757 		} else {
    758 			ssl_certificate_save(current_cert);
    759 			ssl_certificate_destroy(current_cert);
    760 			ssl_certificate_destroy(known_cert);
    761 			return TRUE;
    762 		}
    763 	} else if (gnutls_x509_crt_get_expiration_time(current_cert->x509_cert) < time(NULL)) {
    764 		gchar *tmp = g_strdup_printf("%s:%d", current_cert->host, current_cert->port);
    765 
    766 		if (warned_expired == NULL)
    767 			warned_expired = g_hash_table_new(g_str_hash, g_str_equal);
    768 
    769 		if (g_hash_table_lookup(warned_expired, tmp)) {
    770 			g_free(tmp);
    771 			ssl_certificate_destroy(current_cert);
    772 			ssl_certificate_destroy(known_cert);
    773 			return TRUE;
    774 		}
    775 
    776 		cert_hook_data.cert = current_cert;
    777 		cert_hook_data.old_cert = NULL;
    778 		cert_hook_data.expired = TRUE;
    779 		cert_hook_data.accept = FALSE;
    780 
    781 		hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
    782 
    783 		if (!cert_hook_data.accept) {
    784 			g_free(tmp);
    785 			ssl_certificate_destroy(current_cert);
    786 			ssl_certificate_destroy(known_cert);
    787 			return FALSE;
    788 		} else {
    789 			g_hash_table_insert(warned_expired, tmp, GINT_TO_POINTER(1));
    790 			ssl_certificate_destroy(current_cert);
    791 			ssl_certificate_destroy(known_cert);
    792 			return TRUE;
    793 		}
    794 	}
    795 
    796 	ssl_certificate_destroy(current_cert);
    797 	ssl_certificate_destroy(known_cert);
    798 	return TRUE;
    799 }
    800 
    801 gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len,
    802 				     const gchar *host, gushort port,
    803 				     gboolean accept_if_valid)
    804 {
    805 	int ncas = 0;
    806 	gnutls_x509_crt_t *cas = NULL;
    807 	gboolean result = FALSE;
    808 	int i;
    809 	gint status;
    810 
    811 	if (claws_ssl_get_cert_file()) {
    812 		FILE *fp = g_fopen(claws_ssl_get_cert_file(), "rb");
    813 		int r = -errno;
    814 
    815 		if (fp) {
    816 			r = gnutls_import_X509_list_fp(fp, GNUTLS_X509_FMT_PEM, &cas, &ncas);
    817 			fclose(fp);
    818 		}
    819 
    820 		if (r < 0)
    821 			g_warning("read certificate file '%s': %s",
    822 				claws_ssl_get_cert_file(),
    823 				gnutls_strerror(r));
    824 	} else {
    825 		debug_print("Can't find SSL ca-certificates file\n");
    826 	}
    827 
    828 
    829 	gnutls_x509_crt_list_verify (certs,
    830                              chain_len,
    831                              cas, ncas,
    832                              NULL, 0,
    833                              GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
    834                              &status);
    835 
    836 	result = ssl_certificate_check(certs[0], status, host, port,
    837 					accept_if_valid);
    838 
    839 	if (result == TRUE) {
    840 		ssl_certificate_save_chain(certs, chain_len, host, port);
    841 	}
    842 
    843 	for (i = 0; i < ncas; i++)
    844 		gnutls_x509_crt_deinit(cas[i]);
    845 	free(cas);
    846 
    847 	return result;
    848 }
    849 
    850 gnutls_x509_crt_t ssl_certificate_get_x509_from_pem_file(const gchar *file)
    851 {
    852 	gnutls_x509_crt_t x509 = NULL;
    853 	if (!file)
    854 		return NULL;
    855 
    856 	if (is_file_exist(file)) {
    857 		FILE *fp = g_fopen(file, "r");
    858 		if (fp) {
    859 			x509 = gnutls_import_X509_fp(fp, GNUTLS_X509_FMT_PEM);
    860 			fclose(fp);
    861 			return x509;
    862 		} else {
    863 			log_error(LOG_PROTOCOL, _("Cannot open certificate file %s: %s\n"),
    864 				  file, g_strerror(errno));
    865 		}
    866 	} else {
    867 		log_error(LOG_PROTOCOL, _("Certificate file %s missing (%s)\n"),
    868 			  file, g_strerror(errno));
    869 	}
    870 	return NULL;
    871 }
    872 
    873 gnutls_x509_privkey_t ssl_certificate_get_pkey_from_pem_file(const gchar *file)
    874 {
    875 	gnutls_x509_privkey_t key = NULL;
    876 	if (!file)
    877 		return NULL;
    878 
    879 	if (is_file_exist(file)) {
    880 		FILE *fp = g_fopen(file, "r");
    881 		if (fp) {
    882 			key = gnutls_import_key_fp(fp, GNUTLS_X509_FMT_PEM);
    883 			fclose(fp);
    884 			return key;
    885 		} else {
    886 			log_error(LOG_PROTOCOL, _("Cannot open key file %s (%s)\n"),
    887 			file, g_strerror(errno));
    888 		}
    889 	} else {
    890 		log_error(LOG_PROTOCOL, _("Key file %s missing (%s)\n"), file,
    891 			  g_strerror(errno));
    892 	}
    893 	return NULL;
    894 }
    895 
    896 /* From GnuTLS lib/gnutls_x509.c */
    897 static int
    898 parse_pkcs12 (gnutls_pkcs12_t p12,
    899 	      const char *password,
    900 	      gnutls_x509_privkey_t * key,
    901 	      gnutls_x509_crt_t * cert)
    902 {
    903   gnutls_pkcs12_bag_t bag = NULL;
    904   int index = 0;
    905   int ret;
    906 
    907   for (;;)
    908     {
    909       int elements_in_bag;
    910       int i;
    911 
    912       ret = gnutls_pkcs12_bag_init (&bag);
    913       if (ret < 0)
    914 	{
    915 	  bag = NULL;
    916 	  goto done;
    917 	}
    918 
    919       ret = gnutls_pkcs12_get_bag (p12, index, bag);
    920       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
    921 	break;
    922       if (ret < 0)
    923 	{
    924 	  goto done;
    925 	}
    926 
    927       ret = gnutls_pkcs12_bag_get_type (bag, 0);
    928       if (ret < 0)
    929 	{
    930 	  goto done;
    931 	}
    932 
    933       if (ret == GNUTLS_BAG_ENCRYPTED)
    934 	{
    935 	  ret = gnutls_pkcs12_bag_decrypt (bag, password);
    936 	  if (ret < 0)
    937 	    {
    938 	      goto done;
    939 	    }
    940 	}
    941 
    942       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
    943       if (elements_in_bag < 0)
    944 	{
    945 	  goto done;
    946 	}
    947 
    948       for (i = 0; i < elements_in_bag; i++)
    949 	{
    950 	  int type;
    951 	  gnutls_datum_t data;
    952 
    953 	  type = gnutls_pkcs12_bag_get_type (bag, i);
    954 	  if (type < 0)
    955 	    {
    956 	      goto done;
    957 	    }
    958 
    959 	  ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
    960 	  if (ret < 0)
    961 	    {
    962 	      goto done;
    963 	    }
    964 
    965 	  switch (type)
    966 	    {
    967 	    case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
    968 	    case GNUTLS_BAG_PKCS8_KEY:
    969 	      ret = gnutls_x509_privkey_init (key);
    970 	      if (ret < 0)
    971 		{
    972 		  goto done;
    973 		}
    974 
    975 	      ret = gnutls_x509_privkey_import_pkcs8
    976 		(*key, &data, GNUTLS_X509_FMT_DER, password,
    977 		 type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
    978 	      if (ret < 0)
    979 		{
    980 		  goto done;
    981 		}
    982 	      break;
    983 
    984 	    case GNUTLS_BAG_CERTIFICATE:
    985 	      ret = gnutls_x509_crt_init (cert);
    986 	      if (ret < 0)
    987 		{
    988 		  goto done;
    989 		}
    990 
    991 	      ret =
    992 		gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
    993 	      if (ret < 0)
    994 		{
    995 		  goto done;
    996 		}
    997 	      break;
    998 
    999 	    case GNUTLS_BAG_ENCRYPTED:
   1000 	      /* XXX Bother to recurse one level down?  Unlikely to
   1001 	         use the same password anyway. */
   1002 	    case GNUTLS_BAG_EMPTY:
   1003 	    default:
   1004 	      break;
   1005 	    }
   1006 	}
   1007 
   1008       index++;
   1009       gnutls_pkcs12_bag_deinit (bag);
   1010     }
   1011 
   1012   ret = 0;
   1013 
   1014 done:
   1015   if (bag)
   1016     gnutls_pkcs12_bag_deinit (bag);
   1017 
   1018   return ret;
   1019 }
   1020 void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
   1021 			gnutls_x509_crt_t *x509, gnutls_x509_privkey_t *pkey)
   1022 {
   1023 	gnutls_pkcs12_t p12 = NULL;
   1024 
   1025 	int r;
   1026 
   1027 	*x509 = NULL;
   1028 	*pkey = NULL;
   1029 	if (!file)
   1030 		return;
   1031 
   1032 	if (is_file_exist(file)) {
   1033 		FILE *fp = g_fopen(file, "r");
   1034 		if (fp) {
   1035 			p12 = gnutls_import_PKCS12_fp(fp, GNUTLS_X509_FMT_DER);
   1036 			fclose(fp);
   1037 			if (!p12) {
   1038 				log_error(LOG_PROTOCOL, _("Failed to read P12 certificate file %s\n"), file);
   1039 			}
   1040 		} else {
   1041 			log_error(LOG_PROTOCOL, _("Cannot open P12 certificate file %s (%s)\n"),
   1042 				  file, g_strerror(errno));
   1043 		}
   1044 	} else {
   1045 		log_error(LOG_PROTOCOL, _("P12 Certificate file %s missing (%s)\n"), file,
   1046 			  g_strerror(errno));
   1047 	}
   1048 	if (p12 != NULL) {
   1049 		if ((r = parse_pkcs12(p12, password, pkey, x509)) == 0) {
   1050 			debug_print("got p12\n");
   1051 		} else {
   1052 			log_error(LOG_PROTOCOL, "%s\n", gnutls_strerror(r));
   1053 		}
   1054 		gnutls_pkcs12_deinit(p12);
   1055 	}
   1056 }
   1057 
   1058 gboolean ssl_certificate_check_subject_cn(SSLCertificate *cert)
   1059 {
   1060 	return gnutls_x509_crt_check_hostname(cert->x509_cert, cert->host) != 0;
   1061 }
   1062 
   1063 gchar *ssl_certificate_get_subject_cn(SSLCertificate *cert)
   1064 {
   1065 	gchar subject_cn[BUFFSIZE];
   1066 	size_t n = BUFFSIZE;
   1067 
   1068 	if(gnutls_x509_crt_get_dn_by_oid(cert->x509_cert,
   1069 		GNUTLS_OID_X520_COMMON_NAME, 0, 0, subject_cn, &n))
   1070 		return g_strdup(_("<not in certificate>"));
   1071 
   1072 	return g_strdup(subject_cn);
   1073 }