a25e891e1ed1414a98e7623db637092080011fcb
[claws.git] / src / common / ssl_certificate.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 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 #include "claws-features.h"
24 #endif
25
26 #ifdef USE_GNUTLS
27 #include <gnutls/gnutls.h>
28 #include <gnutls/x509.h>
29 #include <gnutls/pkcs12.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <stdio.h>
35 #include <glib.h>
36 #include <glib/gi18n.h>
37 #ifdef G_OS_WIN32
38 #  include <winsock2.h>
39 #else
40 #  include <sys/socket.h>
41 #  include <netinet/in.h>
42 #  include <netdb.h>
43 #endif /* G_OS_WIN32 */
44 #include "ssl_certificate.h"
45 #include "utils.h"
46 #include "log.h"
47 #include "socket.h"
48 #include "hooks.h"
49 #include "defs.h"
50
51 static GHashTable *warned_expired = NULL;
52
53 gboolean prefs_common_unsafe_ssl_certs(void);
54
55 static gchar *get_certificate_path(const gchar *host, const gchar *port, const gchar *fp)
56 {
57         if (fp != NULL && prefs_common_unsafe_ssl_certs())
58                 return g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 
59                           "certs", G_DIR_SEPARATOR_S,
60                           host, ".", port, ".", fp, ".cert", NULL);
61         else 
62                 return g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 
63                           "certs", G_DIR_SEPARATOR_S,
64                           host, ".", port, ".cert", NULL);
65 }
66
67 char * readable_fingerprint(unsigned char *src, int len) 
68 {
69         int i=0;
70         char * ret;
71         
72         if (src == NULL)
73                 return NULL;
74         ret = g_strdup("");
75         while (i < len) {
76                 char *tmp2;
77                 if(i>0)
78                         tmp2 = g_strdup_printf("%s:%02X", ret, src[i]);
79                 else
80                         tmp2 = g_strdup_printf("%02X", src[i]);
81                 g_free(ret);
82                 ret = g_strdup(tmp2);
83                 g_free(tmp2);
84                 i++;
85         }
86         return ret;
87 }
88
89 #if USE_GNUTLS
90 static gnutls_x509_crt x509_crt_copy(gnutls_x509_crt src)
91 {
92     int ret;
93     size_t size;
94     gnutls_datum tmp;
95     gnutls_x509_crt dest;
96     
97     if (gnutls_x509_crt_init(&dest) != 0) {
98         g_warning("couldn't gnutls_x509_crt_init\n");
99         return NULL;
100     }
101
102     if (gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, NULL, &size) 
103         != GNUTLS_E_SHORT_MEMORY_BUFFER) {
104         g_warning("couldn't gnutls_x509_crt_export to get size\n");
105         gnutls_x509_crt_deinit(dest);
106         return NULL;
107     }
108
109     tmp.data = malloc(size);
110     memset(tmp.data, 0, size);
111     ret = gnutls_x509_crt_export(src, GNUTLS_X509_FMT_DER, tmp.data, &size);
112     if (ret == 0) {
113         tmp.size = size;
114         ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER);
115         if (ret) {
116                 g_warning("couldn't gnutls_x509_crt_import for real (%d %s)\n", ret, gnutls_strerror(ret));
117                 gnutls_x509_crt_deinit(dest);
118                 dest = NULL;
119         }
120     } else {
121         g_warning("couldn't gnutls_x509_crt_export for real (%d %s)\n", ret, gnutls_strerror(ret));
122         gnutls_x509_crt_deinit(dest);
123         dest = NULL;
124     }
125
126     free(tmp.data);
127     return dest;
128 }
129 #endif
130
131 static SSLCertificate *ssl_certificate_new(gnutls_x509_crt x509_cert, const gchar *host, gushort port)
132 {
133         SSLCertificate *cert = g_new0(SSLCertificate, 1);
134         size_t n;
135         unsigned char md[128];  
136
137         if (host == NULL || x509_cert == NULL) {
138                 ssl_certificate_destroy(cert);
139                 return NULL;
140         }
141         cert->x509_cert = x509_crt_copy(x509_cert);
142         cert->status = (guint)-1;
143         cert->host = g_strdup(host);
144         cert->port = port;
145         
146         /* fingerprint */
147         n = 128;
148         gnutls_x509_crt_get_fingerprint(cert->x509_cert, GNUTLS_DIG_MD5, md, &n);
149         cert->fingerprint = readable_fingerprint(md, (int)n);
150         return cert;
151 }
152
153 #ifdef USE_GNUTLS
154 static void gnutls_i2d_X509_fp(FILE *fp, gnutls_x509_crt x509_cert)
155 {
156         char output[10*1024];
157         size_t cert_size = 10*1024;
158         int r;
159         
160         if ((r = gnutls_x509_crt_export(x509_cert, GNUTLS_X509_FMT_DER, output, &cert_size)) < 0) {
161                 g_warning("couldn't export cert %s (%zd)\n", gnutls_strerror(r), cert_size);
162                 return;
163         }
164         debug_print("writing %zd bytes\n",cert_size);
165         if (fwrite(&output, 1, cert_size, fp) < cert_size) {
166                 g_warning("failed to write cert\n");
167         }
168 }
169
170 size_t gnutls_i2d_X509(gnutls_x509_crt x509_cert, unsigned char **output)
171 {
172         size_t cert_size = 10*1024;
173         int r;
174         
175         if (output == NULL)
176                 return 0;
177         
178         *output = malloc(cert_size);
179
180         if ((r = gnutls_x509_crt_export(x509_cert, GNUTLS_X509_FMT_DER, *output, &cert_size)) < 0) {
181                 g_warning("couldn't export cert %s (%zd)\n", gnutls_strerror(r), cert_size);
182                 free(*output);
183                 *output = NULL;
184                 return 0;
185         }
186         return cert_size;
187 }
188
189 size_t gnutls_i2d_PrivateKey(gnutls_x509_privkey pkey, unsigned char **output)
190 {
191         size_t key_size = 10*1024;
192         int r;
193         
194         if (output == NULL)
195                 return 0;
196         
197         *output = malloc(key_size);
198
199         if ((r = gnutls_x509_privkey_export(pkey, GNUTLS_X509_FMT_DER, *output, &key_size)) < 0) {
200                 g_warning("couldn't export key %s (%zd)\n", gnutls_strerror(r), key_size);
201                 free(*output);
202                 *output = NULL;
203                 return 0;
204         }
205         return key_size;
206 }
207
208 static gnutls_x509_crt gnutls_d2i_X509_fp(FILE *fp, int format)
209 {
210         gnutls_x509_crt cert = NULL;
211         gnutls_datum tmp;
212         struct stat s;
213         int r;
214         if (fstat(fileno(fp), &s) < 0) {
215                 perror("fstat");
216                 return NULL;
217         }
218         tmp.data = malloc(s.st_size);
219         memset(tmp.data, 0, s.st_size);
220         tmp.size = s.st_size;
221         if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
222                 perror("fread");
223                 free(tmp.data);
224                 return NULL;
225         }
226
227         gnutls_x509_crt_init(&cert);
228         if ((r = gnutls_x509_crt_import(cert, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM)) < 0) {
229                 debug_print("cert import failed: %s\n", gnutls_strerror(r));
230                 gnutls_x509_crt_deinit(cert);
231                 cert = NULL;
232         }
233         free(tmp.data);
234         debug_print("got cert! %p\n", cert);
235         return cert;
236 }
237
238 static gnutls_x509_privkey gnutls_d2i_key_fp(FILE *fp, int format)
239 {
240         gnutls_x509_privkey key = NULL;
241         gnutls_datum tmp;
242         struct stat s;
243         int r;
244         if (fstat(fileno(fp), &s) < 0) {
245                 perror("fstat");
246                 return NULL;
247         }
248         tmp.data = malloc(s.st_size);
249         memset(tmp.data, 0, s.st_size);
250         tmp.size = s.st_size;
251         if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
252                 perror("fread");
253                 free(tmp.data);
254                 return NULL;
255         }
256
257         gnutls_x509_privkey_init(&key);
258         if ((r = gnutls_x509_privkey_import(key, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM)) < 0) {
259                 debug_print("key import failed: %s\n", gnutls_strerror(r));
260                 gnutls_x509_privkey_deinit(key);
261                 key = NULL;
262         }
263         free(tmp.data);
264         debug_print("got key! %p\n", key);
265         return key;
266 }
267
268 static gnutls_pkcs12_t gnutls_d2i_PKCS12_fp(FILE *fp, int format)
269 {
270         gnutls_pkcs12_t p12 = NULL;
271         gnutls_datum tmp;
272         struct stat s;
273         int r;
274         if (fstat(fileno(fp), &s) < 0) {
275                 perror("fstat");
276                 return NULL;
277         }
278         tmp.data = malloc(s.st_size);
279         memset(tmp.data, 0, s.st_size);
280         tmp.size = s.st_size;
281         if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
282                 perror("fread");
283                 free(tmp.data);
284                 return NULL;
285         }
286
287         gnutls_pkcs12_init(&p12);
288
289         if ((r = gnutls_pkcs12_import(p12, &tmp, (format == 0)?GNUTLS_X509_FMT_DER:GNUTLS_X509_FMT_PEM,0)) < 0) {
290                 g_warning("p12 import failed: %s\n", gnutls_strerror(r));
291                 gnutls_pkcs12_deinit(p12);
292                 p12 = NULL;
293         }
294         free(tmp.data);
295         debug_print("got p12! %p\n", p12);
296         return p12;
297 }
298
299 #endif
300
301 static void ssl_certificate_save (SSLCertificate *cert)
302 {
303         gchar *file, *port;
304         FILE *fp;
305
306         file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 
307                           "certs", G_DIR_SEPARATOR_S, NULL);
308         
309         if (!is_dir_exist(file))
310                 make_dir_hier(file);
311         g_free(file);
312
313         port = g_strdup_printf("%d", cert->port);
314         file = get_certificate_path(cert->host, port, cert->fingerprint);
315
316         g_free(port);
317         fp = g_fopen(file, "wb");
318         if (fp == NULL) {
319                 g_free(file);
320                 debug_print("Can't save certificate !\n");
321                 return;
322         }
323 #ifdef USE_GNUTLS
324         gnutls_i2d_X509_fp(fp, cert->x509_cert);
325 #else
326         i2d_X509_fp(fp, cert->x509_cert);
327 #endif
328         g_free(file);
329         fclose(fp);
330
331 }
332
333 void ssl_certificate_destroy(SSLCertificate *cert) 
334 {
335         if (cert == NULL)
336                 return;
337
338         if (cert->x509_cert)
339                 gnutls_x509_crt_deinit(cert->x509_cert);
340         g_free(cert->host);
341         g_free(cert->fingerprint);
342         g_free(cert);
343         cert = NULL;
344 }
345
346 void ssl_certificate_delete_from_disk(SSLCertificate *cert)
347 {
348         gchar *buf;
349         gchar *file;
350         buf = g_strdup_printf("%d", cert->port);
351         file = get_certificate_path(cert->host, buf, cert->fingerprint);
352         claws_unlink (file);
353         g_free(file);
354         g_free(buf);
355 }
356
357 SSLCertificate *ssl_certificate_find (const gchar *host, gushort port, const gchar *fingerprint)
358 {
359         gchar *file = NULL;
360         gchar *buf;
361         SSLCertificate *cert = NULL;
362         gnutls_x509_crt tmp_x509;
363         FILE *fp = NULL;
364         gboolean must_rename = FALSE;
365
366         buf = g_strdup_printf("%d", port);
367         
368         if (fingerprint != NULL) {
369                 file = get_certificate_path(host, buf, fingerprint);
370                 fp = g_fopen(file, "rb");
371         }
372         if (fp == NULL) {
373                 /* see if we have the old one */
374                 debug_print("didn't get %s\n", file);
375                 g_free(file);
376                 file = get_certificate_path(host, buf, NULL);
377                 fp = g_fopen(file, "rb");
378
379                 if (fp) {
380                         debug_print("got %s\n", file);
381                         must_rename = (fingerprint != NULL);
382                 }
383         } else {
384                 debug_print("got %s first try\n", file);
385         }
386         if (fp == NULL) {
387                 g_free(file);
388                 g_free(buf);
389                 return NULL;
390         }
391         
392         if ((tmp_x509 = gnutls_d2i_X509_fp(fp, 0)) != NULL) {
393                 cert = ssl_certificate_new(tmp_x509, host, port);
394                 debug_print("got cert %p\n", cert);
395                 gnutls_x509_crt_deinit(tmp_x509);
396         }
397         fclose(fp);
398         g_free(file);
399         
400         if (must_rename) {
401                 gchar *old = get_certificate_path(host, buf, NULL);
402                 gchar *new = get_certificate_path(host, buf, fingerprint);
403                 if (strcmp(old, new))
404                         move_file(old, new, TRUE);
405                 g_free(old);
406                 g_free(new);
407         }
408         g_free(buf);
409
410         return cert;
411 }
412
413 static gboolean ssl_certificate_compare (SSLCertificate *cert_a, SSLCertificate *cert_b)
414 {
415         char *output_a;
416         char *output_b;
417         size_t cert_size_a = 0, cert_size_b = 0;
418         int r;
419
420         if (cert_a == NULL || cert_b == NULL)
421                 return FALSE;
422
423         if ((r = gnutls_x509_crt_export(cert_a->x509_cert, GNUTLS_X509_FMT_DER, NULL, &cert_size_a)) 
424             != GNUTLS_E_SHORT_MEMORY_BUFFER) {
425                 g_warning("couldn't gnutls_x509_crt_export to get size a %s\n", gnutls_strerror(r));
426                 return FALSE;
427         }
428
429         if ((r = gnutls_x509_crt_export(cert_b->x509_cert, GNUTLS_X509_FMT_DER, NULL, &cert_size_b))
430             != GNUTLS_E_SHORT_MEMORY_BUFFER) {
431                 g_warning("couldn't gnutls_x509_crt_export to get size b %s\n", gnutls_strerror(r));
432                 return FALSE;
433         }
434
435         output_a = g_malloc(cert_size_a);
436         output_b = g_malloc(cert_size_b);
437         if ((r = gnutls_x509_crt_export(cert_a->x509_cert, GNUTLS_X509_FMT_DER, output_a, &cert_size_a)) < 0) {
438                 g_warning("couldn't gnutls_x509_crt_export a %s\n", gnutls_strerror(r));
439                 g_free(output_a);
440                 g_free(output_b);
441                 return FALSE;
442         }
443         if ((r = gnutls_x509_crt_export(cert_b->x509_cert, GNUTLS_X509_FMT_DER, output_b, &cert_size_b)) < 0) {
444                 g_warning("couldn't gnutls_x509_crt_export b %s\n", gnutls_strerror(r));
445                 g_free(output_a);
446                 g_free(output_b);
447                 return FALSE;
448         }
449         if (cert_size_a != cert_size_b) {
450                 g_warning("size differ %zd %zd\n", cert_size_a, cert_size_b);
451                 g_free(output_a);
452                 g_free(output_b);
453                 return FALSE;
454         }
455         if (memcmp(output_a, output_b, cert_size_a)) {
456                 g_warning("contents differ\n");
457                 g_free(output_a);
458                 g_free(output_b);
459                 return FALSE;
460         }
461         g_free(output_a);
462         g_free(output_b);
463         
464         return TRUE;
465 }
466
467 static guint check_cert(gnutls_x509_crt cert)
468 {
469         gnutls_x509_crt *ca_list;
470         unsigned int max = 512;
471         unsigned int flags = 0;
472         gnutls_datum tmp;
473         struct stat s;
474         int r, i;
475         unsigned int status;
476         FILE *fp;
477
478         if (claws_ssl_get_cert_file())
479                 fp = g_fopen(claws_ssl_get_cert_file(), "r");
480         else
481                 return (guint)-1;
482
483         if (fstat(fileno(fp), &s) < 0) {
484                 perror("fstat");
485                 fclose(fp);
486                 return (guint)-1;
487         }
488
489         ca_list=(gnutls_x509_crt_t*)malloc(max*sizeof(gnutls_x509_crt_t));
490         tmp.data = malloc(s.st_size);
491         memset(tmp.data, 0, s.st_size);
492         tmp.size = s.st_size;
493         if (fread (tmp.data, 1, s.st_size, fp) < s.st_size) {
494                 perror("fread");
495                 free(tmp.data);
496                 free(ca_list);
497                 fclose(fp);
498                 return (guint)-1;
499         }
500
501         if ((r = gnutls_x509_crt_list_import(ca_list, &max, 
502                         &tmp, GNUTLS_X509_FMT_PEM, flags)) < 0) {
503                 debug_print("cert import failed: %s\n", gnutls_strerror(r));
504                 free(tmp.data);
505                 free(ca_list);
506                 fclose(fp);
507                 return (guint)-1;
508         }
509         free(tmp.data);
510         debug_print("got %d certs in ca_list! %p\n", max, &ca_list);
511         r = gnutls_x509_crt_verify(cert, ca_list, max, flags, &status);
512         fclose(fp);
513
514         for (i = 0; i < max; i++)
515                 gnutls_x509_crt_deinit(ca_list[i]);
516         free(ca_list);
517
518         if (r < 0)
519                 return (guint)-1;
520         else
521                 return status;
522
523 }
524
525 char *ssl_certificate_check_signer (gnutls_x509_crt cert, guint status) 
526 {
527         if (status == (guint)-1) {
528                 status = check_cert(cert);
529                 if (status == -1)
530                         return g_strdup(_("Uncheckable"));
531         }
532         if (status & GNUTLS_CERT_INVALID) {
533                 if (gnutls_x509_crt_check_issuer(cert, cert))
534                         return g_strdup(_("Self-signed certificate"));
535         }
536         if (status & GNUTLS_CERT_REVOKED)
537                 return g_strdup(_("Revoked certificate"));
538         if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
539                 return g_strdup(_("No certificate issuer found"));
540         if (status & GNUTLS_CERT_SIGNER_NOT_CA)
541                 return g_strdup(_("Certificate issuer is not a CA"));
542
543
544         return NULL;
545 }
546
547 gboolean ssl_certificate_check (gnutls_x509_crt x509_cert, guint status, const gchar *host, gushort port)
548 {
549         SSLCertificate *current_cert = NULL;
550         SSLCertificate *known_cert;
551         SSLCertHookData cert_hook_data;
552         gchar *fingerprint;
553         size_t n;
554         unsigned char md[128];  
555
556         current_cert = ssl_certificate_new(x509_cert, host, port);
557         
558         if (current_cert == NULL) {
559                 debug_print("Buggy certificate !\n");
560                 return FALSE;
561         }
562
563         current_cert->status = status;
564         /* fingerprint */
565         n = 128;
566         gnutls_x509_crt_get_fingerprint(x509_cert, GNUTLS_DIG_MD5, md, &n);
567         fingerprint = readable_fingerprint(md, n);
568
569         known_cert = ssl_certificate_find(host, port, fingerprint);
570
571         g_free(fingerprint);
572
573         if (known_cert == NULL) {
574                 cert_hook_data.cert = current_cert;
575                 cert_hook_data.old_cert = NULL;
576                 cert_hook_data.expired = FALSE;
577                 cert_hook_data.accept = FALSE;
578                 
579                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
580                 
581                 if (!cert_hook_data.accept) {
582                         ssl_certificate_destroy(current_cert);
583                         return FALSE;
584                 } else {
585                         ssl_certificate_save(current_cert);
586                         ssl_certificate_destroy(current_cert);
587                         return TRUE;
588                 }
589         } else if (!ssl_certificate_compare (current_cert, known_cert)) {
590                 cert_hook_data.cert = current_cert;
591                 cert_hook_data.old_cert = known_cert;
592                 cert_hook_data.expired = FALSE;
593                 cert_hook_data.accept = FALSE;
594                 
595                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
596
597                 if (!cert_hook_data.accept) {
598                         ssl_certificate_destroy(current_cert);
599                         ssl_certificate_destroy(known_cert);
600                         return FALSE;
601                 } else {
602                         ssl_certificate_save(current_cert);
603                         ssl_certificate_destroy(current_cert);
604                         ssl_certificate_destroy(known_cert);
605                         return TRUE;
606                 }
607         } else if (gnutls_x509_crt_get_expiration_time(current_cert->x509_cert) < time(NULL)) {
608                 gchar *tmp = g_strdup_printf("%s:%d", current_cert->host, current_cert->port);
609                 
610                 if (warned_expired == NULL)
611                         warned_expired = g_hash_table_new(g_str_hash, g_str_equal);
612                 
613                 if (g_hash_table_lookup(warned_expired, tmp)) {
614                         g_free(tmp);
615                         ssl_certificate_destroy(current_cert);
616                         ssl_certificate_destroy(known_cert);
617                         return TRUE;
618                 }
619                         
620                 cert_hook_data.cert = current_cert;
621                 cert_hook_data.old_cert = NULL;
622                 cert_hook_data.expired = TRUE;
623                 cert_hook_data.accept = FALSE;
624                 
625                 hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
626
627                 if (!cert_hook_data.accept) {
628                         g_free(tmp);
629                         ssl_certificate_destroy(current_cert);
630                         ssl_certificate_destroy(known_cert);
631                         return FALSE;
632                 } else {
633                         g_hash_table_insert(warned_expired, tmp, GINT_TO_POINTER(1));
634                         ssl_certificate_destroy(current_cert);
635                         ssl_certificate_destroy(known_cert);
636                         return TRUE;
637                 }
638         }
639
640         ssl_certificate_destroy(current_cert);
641         ssl_certificate_destroy(known_cert);
642         return TRUE;
643 }
644
645 gnutls_x509_crt ssl_certificate_get_x509_from_pem_file(const gchar *file)
646 {
647         gnutls_x509_crt x509 = NULL;
648         if (!file)
649                 return NULL;
650         
651         if (is_file_exist(file)) {
652                 FILE *fp = g_fopen(file, "r");
653                 if (fp) {
654                         x509 = gnutls_d2i_X509_fp(fp, 1);
655                         fclose(fp);
656                         return x509;
657                 }
658         } else {
659                 log_error(LOG_PROTOCOL, _("Cannot open certificate file %s\n"), file);
660         }
661         return NULL;
662 }
663
664 gnutls_x509_privkey ssl_certificate_get_pkey_from_pem_file(const gchar *file)
665 {
666         gnutls_x509_privkey key = NULL;
667         if (!file)
668                 return NULL;
669         
670         if (is_file_exist(file)) {
671                 FILE *fp = g_fopen(file, "r");
672                 if (fp) {
673                         key = gnutls_d2i_key_fp(fp, 1);
674                         fclose(fp);
675                         return key;
676                 }
677         } else {
678                 log_error(LOG_PROTOCOL, _("Cannot open key file %s\n"), file);
679         }
680         return NULL;
681 }
682
683 /* From GnuTLS lib/gnutls_x509.c */
684 static int
685 parse_pkcs12 (gnutls_pkcs12_t p12,
686               const char *password,
687               gnutls_x509_privkey * key,
688               gnutls_x509_crt_t * cert)
689 {
690   gnutls_pkcs12_bag_t bag = NULL;
691   int index = 0;
692   int ret;
693
694   for (;;)
695     {
696       int elements_in_bag;
697       int i;
698
699       ret = gnutls_pkcs12_bag_init (&bag);
700       if (ret < 0)
701         {
702           bag = NULL;
703           goto done;
704         }
705
706       ret = gnutls_pkcs12_get_bag (p12, index, bag);
707       if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
708         break;
709       if (ret < 0)
710         {
711           goto done;
712         }
713
714       ret = gnutls_pkcs12_bag_get_type (bag, 0);
715       if (ret < 0)
716         {
717           goto done;
718         }
719
720       if (ret == GNUTLS_BAG_ENCRYPTED)
721         {
722           ret = gnutls_pkcs12_bag_decrypt (bag, password);
723           if (ret < 0)
724             {
725               goto done;
726             }
727         }
728
729       elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
730       if (elements_in_bag < 0)
731         {
732           goto done;
733         }
734
735       for (i = 0; i < elements_in_bag; i++)
736         {
737           int type;
738           gnutls_datum data;
739
740           type = gnutls_pkcs12_bag_get_type (bag, i);
741           if (type < 0)
742             {
743               goto done;
744             }
745
746           ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
747           if (ret < 0)
748             {
749               goto done;
750             }
751
752           switch (type)
753             {
754             case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
755             case GNUTLS_BAG_PKCS8_KEY:
756               ret = gnutls_x509_privkey_init (key);
757               if (ret < 0)
758                 {
759                   goto done;
760                 }
761
762               ret = gnutls_x509_privkey_import_pkcs8
763                 (*key, &data, GNUTLS_X509_FMT_DER, password,
764                  type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
765               if (ret < 0)
766                 {
767                   goto done;
768                 }
769               break;
770
771             case GNUTLS_BAG_CERTIFICATE:
772               ret = gnutls_x509_crt_init (cert);
773               if (ret < 0)
774                 {
775                   goto done;
776                 }
777
778               ret =
779                 gnutls_x509_crt_import (*cert, &data, GNUTLS_X509_FMT_DER);
780               if (ret < 0)
781                 {
782                   goto done;
783                 }
784               break;
785
786             case GNUTLS_BAG_ENCRYPTED:
787               /* XXX Bother to recurse one level down?  Unlikely to
788                  use the same password anyway. */
789             case GNUTLS_BAG_EMPTY:
790             default:
791               break;
792             }
793         }
794
795       index++;
796       gnutls_pkcs12_bag_deinit (bag);
797     }
798
799   ret = 0;
800
801 done:
802   if (bag)
803     gnutls_pkcs12_bag_deinit (bag);
804
805   return ret;
806 }
807 void ssl_certificate_get_x509_and_pkey_from_p12_file(const gchar *file, const gchar *password,
808                         gnutls_x509_crt *x509, gnutls_x509_privkey *pkey)
809 {
810         gnutls_pkcs12_t p12 = NULL;
811
812         int r;
813
814         *x509 = NULL;
815         *pkey = NULL;
816         if (!file)
817                 return;
818
819         if (is_file_exist(file)) {
820                 FILE *fp = g_fopen(file, "r");
821                 if (fp) {
822                         p12 = gnutls_d2i_PKCS12_fp(fp, 0);
823                         fclose(fp);
824                 }
825         } else {
826                 log_error(LOG_PROTOCOL, _("Cannot open certificate file %s\n"), file);
827         }
828         if (p12 != NULL) {
829                 if ((r = parse_pkcs12(p12, password, pkey, x509)) == 0) {
830                         debug_print("got p12\n");
831                 } else {
832                         log_error(LOG_PROTOCOL, "%s\n", gnutls_strerror(r));
833                 }
834                 gnutls_pkcs12_deinit(p12);
835         }
836 }
837 #endif /* USE_GNUTLS */