2008-10-10 [colin] 3.6.1cvs1
[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 char *ssl_certificate_check_signer (gnutls_x509_crt cert, guint status) 
653 {
654         if (status == (guint)-1)
655                 return g_strdup(_("Uncheckable"));
656
657         if (status & GNUTLS_CERT_INVALID) {
658                 if (gnutls_x509_crt_check_issuer(cert, cert))
659                         return g_strdup(_("Self-signed certificate"));
660         }
661         if (status & GNUTLS_CERT_REVOKED)
662                 return g_strdup(_("Revoked certificate"));
663         if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
664                 return g_strdup(_("No certificate issuer found"));
665         if (status & GNUTLS_CERT_SIGNER_NOT_CA)
666                 return g_strdup(_("Certificate issuer is not a CA"));
667
668
669         return NULL;
670 }
671 #endif
672
673 #if USE_OPENSSL
674 gboolean ssl_certificate_check (X509 *x509_cert, gchar *fqdn, gchar *host, gushort port)
675 #else
676 gboolean ssl_certificate_check (gnutls_x509_crt x509_cert, guint status, gchar *fqdn, gchar *host, gushort port)
677 #endif
678 {
679         SSLCertificate *current_cert = NULL;
680         SSLCertificate *known_cert;
681         SSLCertHookData cert_hook_data;
682         gchar *fqdn_host = NULL;        
683         gchar *fingerprint;
684 #ifdef USE_OPENSSL
685         unsigned int n;
686 #else
687         size_t n;
688 #endif
689         unsigned char md[128];  
690
691         if (fqdn)
692                 fqdn_host = g_strdup(fqdn);
693         else if (host)
694                 fqdn_host = get_fqdn(host);
695         else {
696                 g_warning("no host!\n");
697                 return FALSE;
698         }
699                 
700         current_cert = ssl_certificate_new_lookup(x509_cert, fqdn_host, port, FALSE);
701         
702         if (current_cert == NULL) {
703                 debug_print("Buggy certificate !\n");
704                 g_free(fqdn_host);
705                 return FALSE;
706         }
707
708 #if USE_GNUTLS
709         current_cert->status = status;
710 #endif
711         /* fingerprint */
712 #if USE_OPENSSL
713         X509_digest(x509_cert, EVP_md5(), md, &n);
714         fingerprint = readable_fingerprint(md, (int)n);
715 #else
716         n = 128;
717         gnutls_x509_crt_get_fingerprint(x509_cert, GNUTLS_DIG_MD5, md, &n);
718         fingerprint = readable_fingerprint(md, n);
719 #endif
720
721         known_cert = ssl_certificate_find_lookup (fqdn_host, port, fingerprint, FALSE);
722
723         g_free(fingerprint);
724         g_free(fqdn_host);
725
726         if (known_cert == NULL) {
727                 cert_hook_data.cert = current_cert;
728                 cert_hook_data.old_cert = NULL;
729                 cert_hook_data.expired = FALSE;
730                 cert_hook_data.accept = FALSE;
731                 
732                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
733                 
734                 if (!cert_hook_data.accept) {
735                         ssl_certificate_destroy(current_cert);
736                         return FALSE;
737                 } else {
738                         ssl_certificate_save(current_cert);
739                         ssl_certificate_destroy(current_cert);
740                         return TRUE;
741                 }
742         } else if (!ssl_certificate_compare (current_cert, known_cert)) {
743                 cert_hook_data.cert = current_cert;
744                 cert_hook_data.old_cert = known_cert;
745                 cert_hook_data.expired = FALSE;
746                 cert_hook_data.accept = FALSE;
747                 
748                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
749
750                 if (!cert_hook_data.accept) {
751                         ssl_certificate_destroy(current_cert);
752                         ssl_certificate_destroy(known_cert);
753                         return FALSE;
754                 } else {
755                         ssl_certificate_save(current_cert);
756                         ssl_certificate_destroy(current_cert);
757                         ssl_certificate_destroy(known_cert);
758                         return TRUE;
759                 }
760 #if USE_OPENSSL
761         } else if (asn1toTime(X509_get_notAfter(current_cert->x509_cert)) < time(NULL)) {
762 #else
763         } else if (gnutls_x509_crt_get_expiration_time(current_cert->x509_cert) < time(NULL)) {
764 #endif
765                 gchar *tmp = g_strdup_printf("%s:%d", current_cert->host, current_cert->port);
766                 
767                 if (warned_expired == NULL)
768                         warned_expired = g_hash_table_new(g_str_hash, g_str_equal);
769                 
770                 if (g_hash_table_lookup(warned_expired, tmp)) {
771                         g_free(tmp);
772                         ssl_certificate_destroy(current_cert);
773                         ssl_certificate_destroy(known_cert);
774                         return TRUE;
775                 }
776                         
777                 cert_hook_data.cert = current_cert;
778                 cert_hook_data.old_cert = NULL;
779                 cert_hook_data.expired = TRUE;
780                 cert_hook_data.accept = FALSE;
781                 
782                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
783
784                 if (!cert_hook_data.accept) {
785                         g_free(tmp);
786                         ssl_certificate_destroy(current_cert);
787                         ssl_certificate_destroy(known_cert);
788                         return FALSE;
789                 } else {
790                         g_hash_table_insert(warned_expired, tmp, GINT_TO_POINTER(1));
791                         ssl_certificate_destroy(current_cert);
792                         ssl_certificate_destroy(known_cert);
793                         return TRUE;
794                 }
795         }
796
797         ssl_certificate_destroy(current_cert);
798         ssl_certificate_destroy(known_cert);
799         return TRUE;
800 }
801
802 #if USE_OPENSSL
803 X509 *ssl_certificate_get_x509_from_pem_file(const gchar *file)
804 {
805         X509 *x509 = NULL;
806         if (!file)
807                 return NULL;
808         if (is_file_exist(file)) {
809                 FILE *fp = g_fopen(file, "r");
810                 if (fp) {
811                         x509 = PEM_read_X509(fp, NULL, NULL, NULL);
812                         fclose(fp);
813                         return x509;
814                 }
815         } else {
816                 log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
817         }
818         return NULL;
819 }
820
821 static int ssl_pkey_password_cb(char *buf, int max_len, int flag, void *pwd)
822 {
823         return 0;
824 }
825
826 EVP_PKEY *ssl_certificate_get_pkey_from_pem_file(const gchar *file)
827 {
828         EVP_PKEY *pkey = NULL;
829         if (!file)
830                 return NULL;
831         if (is_file_exist(file)) {
832                 FILE *fp = g_fopen(file, "r");
833                 if (fp) {
834                         pkey = PEM_read_PrivateKey(fp, NULL, ssl_pkey_password_cb, NULL);
835                         fclose(fp);
836                         return pkey;
837                 }
838         } else {
839                 log_error(LOG_PROTOCOL, "Can not open private key file %s\n", file);
840         }
841         return NULL;
842 }
843
844 void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
845                         X509 **x509, EVP_PKEY **pkey)
846 {
847         PKCS12 *p12 = NULL;
848         *x509 = NULL;
849         *pkey = NULL;
850
851         if (!file)
852                 return;
853
854         if (is_file_exist(file)) {
855                 FILE *fp = g_fopen(file, "r");
856                 if (fp) {
857                         p12 = d2i_PKCS12_fp(fp, NULL);
858                         fclose(fp);
859                 }
860         } else {
861                 log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
862         }
863         if (p12 != NULL) {
864                 if (PKCS12_parse(p12, password, pkey, x509, NULL) == 1) {
865                         /* we got the correct password */
866                 } else {
867                         gchar *tmp = NULL;
868                         hooks_invoke(SSL_CERT_GET_PASSWORD, &tmp);
869                         if (PKCS12_parse(p12, tmp, pkey, x509, NULL) == 1) {
870                                 debug_print("got p12\n");
871                         } else {
872                                 log_error(LOG_PROTOCOL, "%s\n", ERR_error_string(ERR_get_error(),NULL));
873                         }
874                 }
875                 PKCS12_free(p12);
876         }
877 }
878 #endif
879
880 #ifdef USE_GNUTLS
881 gnutls_x509_crt ssl_certificate_get_x509_from_pem_file(const gchar *file)
882 {
883         gnutls_x509_crt x509 = NULL;
884         if (!file)
885                 return NULL;
886         
887         if (is_file_exist(file)) {
888                 FILE *fp = g_fopen(file, "r");
889                 if (fp) {
890                         x509 = gnutls_d2i_X509_fp(fp, 1);
891                         fclose(fp);
892                         return x509;
893                 }
894         } else {
895                 log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
896         }
897         return NULL;
898 }
899
900 gnutls_x509_privkey ssl_certificate_get_pkey_from_pem_file(const gchar *file)
901 {
902         gnutls_x509_privkey key = NULL;
903         if (!file)
904                 return NULL;
905         
906         if (is_file_exist(file)) {
907                 FILE *fp = g_fopen(file, "r");
908                 if (fp) {
909                         key = gnutls_d2i_key_fp(fp, 1);
910                         fclose(fp);
911                         return key;
912                 }
913         } else {
914                 log_error(LOG_PROTOCOL, "Can not open key file %s\n", file);
915         }
916         return NULL;
917 }
918
919 /* From GnuTLS lib/gnutls_x509.c */
920 static int
921 parse_pkcs12 (gnutls_pkcs12_t p12,
922               const char *password,
923               gnutls_x509_privkey * key,
924               gnutls_x509_crt_t * cert)
925 {
926   gnutls_pkcs12_bag bag = NULL;
927   int index = 0;
928   int ret;
929
930   for (;;)
931     {
932       int elements_in_bag;
933       int i;
934
935       ret = gnutls_pkcs12_bag_init (&bag);
936       if (ret < 0)
937         {
938           bag = NULL;
939           goto done;
940         }
941
942       ret = gnutls_pkcs12_get_bag (p12, index, bag);
943       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
944         break;
945       if (ret < 0)
946         {
947           goto done;
948         }
949
950       ret = gnutls_pkcs12_bag_get_type (bag, 0);
951       if (ret < 0)
952         {
953           goto done;
954         }
955
956       if (ret == GNUTLS_BAG_ENCRYPTED)
957         {
958           ret = gnutls_pkcs12_bag_decrypt (bag, password);
959           if (ret < 0)
960             {
961               goto done;
962             }
963         }
964
965       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
966       if (elements_in_bag < 0)
967         {
968           goto done;
969         }
970
971       for (i = 0; i < elements_in_bag; i++)
972         {
973           int type;
974           gnutls_datum data;
975
976           type = gnutls_pkcs12_bag_get_type (bag, i);
977           if (type < 0)
978             {
979               goto done;
980             }
981
982           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
983           if (ret < 0)
984             {
985               goto done;
986             }
987
988           switch (type)
989             {
990             case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
991             case GNUTLS_BAG_PKCS8_KEY:
992               ret = gnutls_x509_privkey_init (key);
993               if (ret < 0)
994                 {
995                   goto done;
996                 }
997
998               ret = gnutls_x509_privkey_import_pkcs8
999                 (*key, &data, GNUTLS_X509_FMT_DER, password,
1000                  type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
1001               if (ret < 0)
1002                 {
1003                   goto done;
1004                 }
1005               break;
1006
1007             case GNUTLS_BAG_CERTIFICATE:
1008               ret = gnutls_x509_crt_init (cert);
1009               if (ret < 0)
1010                 {
1011                   goto done;
1012                 }
1013
1014               ret =
1015                 gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
1016               if (ret < 0)
1017                 {
1018                   goto done;
1019                 }
1020               break;
1021
1022             case GNUTLS_BAG_ENCRYPTED:
1023               /* XXX Bother to recurse one level down?  Unlikely to
1024                  use the same password anyway. */
1025             case GNUTLS_BAG_EMPTY:
1026             default:
1027               break;
1028             }
1029         }
1030
1031       index++;
1032       gnutls_pkcs12_bag_deinit (bag);
1033     }
1034
1035   ret = 0;
1036
1037 done:
1038   if (bag)
1039     gnutls_pkcs12_bag_deinit (bag);
1040
1041   return ret;
1042 }
1043 void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
1044                         gnutls_x509_crt *x509, gnutls_x509_privkey *pkey)
1045 {
1046         gnutls_pkcs12_t p12 = NULL;
1047
1048         int r;
1049
1050         *x509 = NULL;
1051         *pkey = NULL;
1052         if (!file)
1053                 return;
1054
1055         if (is_file_exist(file)) {
1056                 FILE *fp = g_fopen(file, "r");
1057                 if (fp) {
1058                         p12 = gnutls_d2i_PKCS12_fp(fp, 0);
1059                         fclose(fp);
1060                 }
1061         } else {
1062                 log_error(LOG_PROTOCOL, "Can not open certificate file %s\n", file);
1063         }
1064         if (p12 != NULL) {
1065                 if ((r = parse_pkcs12(p12, password, pkey, x509)) == 0) {
1066                         debug_print("got p12\n");
1067                 } else {
1068                         log_error(LOG_PROTOCOL, "%s\n", gnutls_strerror(r));
1069                 }
1070                 gnutls_pkcs12_deinit(p12);
1071         }
1072 }
1073 #endif
1074 #endif /* USE_OPENSSL */