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