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