]> git.llucax.com Git - software/mutt-debian.git/blob - mutt_ssl_gnutls.c
removing an unused PATCHES
[software/mutt-debian.git] / mutt_ssl_gnutls.c
1 /* Copyright (C) 2001 Marco d'Itri <md@linux.it>
2  * Copyright (C) 2001-2004 Andrew McDonald <andrew@mcdonald.org.uk>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #ifdef HAVE_GNUTLS_OPENSSL_H
26 #include <gnutls/openssl.h>
27 #endif
28
29 #include "mutt.h"
30 #include "mutt_socket.h"
31 #include "mutt_curses.h"
32 #include "mutt_menu.h"
33 #include "mutt_ssl.h"
34 #include "mutt_regex.h"
35
36 typedef struct _tlssockdata
37 {
38   gnutls_session state;
39   gnutls_certificate_credentials xcred;
40 }
41 tlssockdata;
42
43 /* local prototypes */
44 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len);
45 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len);
46 static int tls_socket_open (CONNECTION* conn);
47 static int tls_socket_close (CONNECTION* conn);
48 static int tls_starttls_close (CONNECTION* conn);
49
50 static int tls_init (void);
51 static int tls_negotiate (CONNECTION* conn);
52 static int tls_check_certificate (CONNECTION* conn);
53
54
55 static int tls_init (void)
56 {
57   static unsigned char init_complete = 0;
58   int err;
59
60   if (init_complete)
61     return 0;
62
63   err = gnutls_global_init();
64   if (err < 0)
65   {
66     mutt_error ("gnutls_global_init: %s", gnutls_strerror(err));
67     mutt_sleep (2);
68     return -1;
69   }
70
71   init_complete = 1;
72   return 0;
73 }
74
75 int mutt_ssl_socket_setup (CONNECTION* conn)
76 {
77   if (tls_init() < 0)
78     return -1;
79
80   conn->conn_open       = tls_socket_open;
81   conn->conn_read       = tls_socket_read;
82   conn->conn_write      = tls_socket_write;
83   conn->conn_close      = tls_socket_close;
84   conn->conn_poll       = raw_socket_poll;
85
86   return 0;
87 }
88
89 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len)
90 {
91   tlssockdata *data = conn->sockdata;
92   int ret;
93
94   if (!data)
95   {
96     mutt_error (_("Error: no TLS socket open"));
97     mutt_sleep (2);
98     return -1;
99   }
100
101   do {
102     ret = gnutls_record_recv (data->state, buf, len);
103     if (ret < 0 && gnutls_error_is_fatal(ret) == 1)
104     {
105       mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
106       mutt_sleep (4);
107       return -1;
108     }
109   }
110   while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
111
112   return ret;
113 }
114
115 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
116 {
117   tlssockdata *data = conn->sockdata;
118   int ret;
119   size_t sent = 0;
120
121   if (!data)
122   {
123     mutt_error (_("Error: no TLS socket open"));
124     mutt_sleep (2);
125     return -1;
126   }
127
128   do
129   {
130     ret = gnutls_record_send (data->state, buf + sent, len - sent);
131     if (ret < 0)
132     {
133       if (gnutls_error_is_fatal(ret) == 1)
134       {
135         mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
136         mutt_sleep (4);
137         return -1;
138       }
139       return ret;
140     }
141     sent += ret;
142   } while (sent < len);
143
144   return sent;
145 }
146
147 static int tls_socket_open (CONNECTION* conn)
148 {
149   if (raw_socket_open (conn) < 0)
150     return -1;
151
152   if (tls_negotiate (conn) < 0)
153   {
154     tls_socket_close (conn);
155     return -1;
156   }
157
158   return 0;
159 }
160
161 int mutt_ssl_starttls (CONNECTION* conn)
162 {
163   if (tls_init() < 0)
164     return -1;
165
166   if (tls_negotiate (conn) < 0)
167     return -1;
168
169   conn->conn_read       = tls_socket_read;
170   conn->conn_write      = tls_socket_write;
171   conn->conn_close      = tls_starttls_close;
172
173   return 0;
174 }
175
176 static void tls_get_client_cert (CONNECTION* conn)
177 {
178   tlssockdata *data = conn->sockdata;
179   const gnutls_datum_t* crtdata;
180   gnutls_x509_crt_t clientcrt;
181   char* dn;
182   char* cn;
183   char* cnend;
184   size_t dnlen;
185
186   /* get our cert CN if we have one */
187   if (!(crtdata = gnutls_certificate_get_ours (data->state)))
188     return;
189
190   if (gnutls_x509_crt_init (&clientcrt) < 0)
191   {
192     dprint (1, (debugfile, "Failed to init gnutls crt\n"));
193     return;
194   }
195   if (gnutls_x509_crt_import (clientcrt, crtdata, GNUTLS_X509_FMT_DER) < 0)
196   {
197     dprint (1, (debugfile, "Failed to import gnutls client crt\n"));
198     goto err_crt;
199   }
200   /* get length of DN */
201   dnlen = 0;
202   gnutls_x509_crt_get_dn (clientcrt, NULL, &dnlen);
203   if (!(dn = calloc (1, dnlen)))
204   {
205     dprint (1, (debugfile, "could not allocate DN\n"));
206     goto err_crt;
207   }
208   gnutls_x509_crt_get_dn (clientcrt, dn, &dnlen);
209   dprint (2, (debugfile, "client certificate DN: %s\n", dn));
210
211   /* extract CN to use as external user name */
212   if (!(cn = strstr (dn, "CN=")))
213   {
214     dprint (1, (debugfile, "no CN found in DN\n"));
215     goto err_dn;
216   }
217   cn += 3;
218
219   if ((cnend = strstr (dn, ",EMAIL=")))
220     *cnend = '\0';
221
222   /* if we are using a client cert, SASL may expect an external auth name */
223   mutt_account_getuser (&conn->account);
224
225 err_dn:
226   FREE (&dn);
227 err_crt:
228   gnutls_x509_crt_deinit (clientcrt);
229 }
230
231 static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
232
233 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
234  *   TLS over the wire, including certificate checks. */
235 static int tls_negotiate (CONNECTION * conn)
236 {
237   tlssockdata *data;
238   int err;
239
240   data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
241   conn->sockdata = data;
242   err = gnutls_certificate_allocate_credentials (&data->xcred);
243   if (err < 0)
244   {
245     FREE(&conn->sockdata);
246     mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
247     mutt_sleep (2);
248     return -1;
249   }
250
251   gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
252                                           GNUTLS_X509_FMT_PEM);
253   /* ignore errors, maybe file doesn't exist yet */
254
255   if (SslCACertFile)
256   {
257     gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
258                                             GNUTLS_X509_FMT_PEM);
259   }
260
261   if (SslClientCert)
262   {
263     dprint (2, (debugfile, "Using client certificate %s\n", SslClientCert));
264     gnutls_certificate_set_x509_key_file (data->xcred, SslClientCert,
265                                           SslClientCert, GNUTLS_X509_FMT_PEM);
266   }
267
268   gnutls_init(&data->state, GNUTLS_CLIENT);
269
270   /* set socket */
271   gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)conn->fd);
272
273   /* disable TLS/SSL protocols as needed */
274   if (!option(OPTTLSV1) && !option(OPTSSLV3))
275   {
276     mutt_error (_("All available protocols for TLS/SSL connection disabled"));
277     goto fail;
278   }
279   else if (!option(OPTTLSV1))
280   {
281     protocol_priority[0] = GNUTLS_SSL3;
282     protocol_priority[1] = 0;
283   }
284   else if (!option(OPTSSLV3))
285   {
286     protocol_priority[0] = GNUTLS_TLS1;
287     protocol_priority[1] = 0;
288   }
289   /*
290   else
291     use the list set above
292   */
293
294   /* We use default priorities (see gnutls documentation),
295      except for protocol version */
296   gnutls_set_default_priority (data->state);
297   gnutls_protocol_set_priority (data->state, protocol_priority);
298
299   if (SslDHPrimeBits > 0)
300   {
301     gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
302   }
303
304 /*
305   gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
306 */
307
308   gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
309
310   err = gnutls_handshake(data->state);
311
312   while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED)
313   {
314     err = gnutls_handshake(data->state);
315   }
316   if (err < 0) {
317     if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
318     {
319       mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
320                  gnutls_alert_get_name(gnutls_alert_get(data->state)));
321     }
322     else
323     {
324       mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
325     }
326     mutt_sleep (2);
327     goto fail;
328   }
329
330   if (!tls_check_certificate(conn))
331     goto fail;
332
333   /* set Security Strength Factor (SSF) for SASL */
334   /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
335   conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
336
337   tls_get_client_cert (conn);
338
339   if (!option(OPTNOCURSES)) {
340     mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
341                   gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)),
342                   gnutls_kx_get_name (gnutls_kx_get (data->state)),
343                   gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
344                   gnutls_mac_get_name (gnutls_mac_get (data->state)));
345     mutt_sleep (0);
346   }
347
348   return 0;
349
350  fail:
351   gnutls_certificate_free_credentials (data->xcred);
352   gnutls_deinit (data->state);
353   FREE(&conn->sockdata);
354   return -1;
355 }
356
357 static int tls_socket_close (CONNECTION* conn)
358 {
359   tlssockdata *data = conn->sockdata;
360   if (data)
361   {
362     gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
363
364     gnutls_certificate_free_credentials (data->xcred);
365     gnutls_deinit (data->state);
366     FREE (&conn->sockdata);
367   }
368
369   return raw_socket_close (conn);
370 }
371
372 static int tls_starttls_close (CONNECTION* conn)
373 {
374   int rc;
375
376   rc = tls_socket_close (conn);
377   conn->conn_read = raw_socket_read;
378   conn->conn_write = raw_socket_write;
379   conn->conn_close = raw_socket_close;
380
381   return rc;
382 }
383
384 #define CERT_SEP "-----BEGIN"
385
386 /* this bit is based on read_ca_file() in gnutls */
387 static int tls_compare_certificates (const gnutls_datum *peercert)
388 {
389   gnutls_datum cert;
390   unsigned char *ptr;
391   FILE *fd1;
392   int ret;
393   gnutls_datum b64_data;
394   unsigned char *b64_data_data;
395   struct stat filestat;
396
397   if (stat(SslCertFile, &filestat) == -1)
398     return 0;
399
400   b64_data.size = filestat.st_size+1;
401   b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
402   b64_data_data[b64_data.size-1] = '\0';
403   b64_data.data = b64_data_data;
404
405   fd1 = fopen(SslCertFile, "r");
406   if (fd1 == NULL) {
407     return 0;
408   }
409
410   b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1);
411   fclose(fd1);
412
413   do {
414     ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
415     if (ret != 0)
416     {
417       FREE (&b64_data_data);
418       return 0;
419     }
420
421     ptr = (unsigned char *)strstr((char*)b64_data.data, CERT_SEP) + 1;
422     ptr = (unsigned char *)strstr((char*)ptr, CERT_SEP);
423
424     b64_data.size = b64_data.size - (ptr - b64_data.data);
425     b64_data.data = ptr;
426
427     if (cert.size == peercert->size)
428     {
429       if (memcmp (cert.data, peercert->data, cert.size) == 0)
430       {
431         /* match found */
432         gnutls_free(cert.data);
433         FREE (&b64_data_data);
434         return 1;
435       }
436     }
437
438     gnutls_free(cert.data);
439   } while (ptr != NULL);
440
441   /* no match found */
442   FREE (&b64_data_data);
443   return 0;
444 }
445
446 static void tls_fingerprint (gnutls_digest_algorithm algo,
447                              char* s, int l, const gnutls_datum* data)
448 {
449   unsigned char md[36];
450   size_t n;
451   int j;
452
453   n = 36;
454
455   if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
456   {
457     snprintf (s, l, _("[unable to calculate]"));
458   }
459   else
460   {
461     for (j = 0; j < (int) n; j++)
462     {
463       char ch[8];
464       snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
465       safe_strcat (s, l, ch);
466     }
467     s[2*n+n/2-1] = '\0'; /* don't want trailing space */
468   }
469 }
470
471 static char *tls_make_date (time_t t, char *s, size_t len)
472 {
473   struct tm *l = gmtime (&t);
474
475   if (l)
476     snprintf (s, len,  "%s, %d %s %d %02d:%02d:%02d UTC",
477               Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
478               l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
479   else
480     strfcpy (s, _("[invalid date]"), len);
481
482   return (s);
483 }
484
485 static int tls_check_stored_hostname (const gnutls_datum *cert,
486                                       const char *hostname)
487 {
488   char buf[80];
489   FILE *fp;
490   char *linestr = NULL;
491   size_t linestrsize;
492   int linenum = 0;
493   regex_t preg;
494   regmatch_t pmatch[3];
495
496   /* try checking against names stored in stored certs file */
497   if ((fp = fopen (SslCertFile, "r")))
498   {
499     if (regcomp(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$", REG_ICASE|REG_EXTENDED) != 0)
500     {
501        regfree(&preg);
502        return 0;
503     }
504
505     buf[0] = '\0';
506     tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
507     while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum)) != NULL)
508     {
509       if(linestr[0] == '#' && linestr[1] == 'H')
510       {
511         if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
512         {
513           linestr[pmatch[1].rm_eo] = '\0';
514           linestr[pmatch[2].rm_eo] = '\0';
515           if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
516               strcmp(linestr + pmatch[2].rm_so, buf) == 0)
517           {
518             regfree(&preg);
519             FREE(&linestr);
520             fclose(fp);
521             return 1;
522           }
523         }
524       }
525     }
526
527     regfree(&preg);
528     fclose(fp);
529   }
530
531   /* not found a matching name */
532   return 0;
533 }
534
535 static int tls_check_one_certificate (const gnutls_datum_t *certdata,
536                                       gnutls_certificate_status certstat,
537                                       const char* hostname, int idx, int len)
538 {
539   gnutls_x509_crt cert;
540   int certerr_hostname = 0;
541   int certerr_expired = 0;
542   int certerr_notyetvalid = 0;
543   int certerr_nottrusted = 0;
544   int certerr_revoked = 0;
545   int certerr_signernotca = 0;
546   char buf[SHORT_STRING];
547   char fpbuf[SHORT_STRING];
548   size_t buflen;
549   char dn_common_name[SHORT_STRING];
550   char dn_email[SHORT_STRING];
551   char dn_organization[SHORT_STRING];
552   char dn_organizational_unit[SHORT_STRING];
553   char dn_locality[SHORT_STRING];
554   char dn_province[SHORT_STRING];
555   char dn_country[SHORT_STRING];
556   time_t t;
557   char datestr[30];
558   MUTTMENU *menu;
559   char helpstr[LONG_STRING];
560   char title[STRING];
561   FILE *fp;
562   gnutls_datum pemdata;
563   int i, row, done, ret;
564
565   if (gnutls_x509_crt_init (&cert) < 0)
566   {
567     mutt_error (_("Error initialising gnutls certificate data"));
568     mutt_sleep (2);
569     return 0;
570   }
571   
572   if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
573   {
574     mutt_error (_("Error processing certificate data"));
575     mutt_sleep (2);
576     gnutls_x509_crt_deinit (cert);
577     return -1;
578   }
579   
580   if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
581     certerr_expired = 1;
582   if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
583     certerr_notyetvalid = 1;
584
585   if (!idx)
586   {
587     if (!gnutls_x509_crt_check_hostname (cert, hostname) &&
588         !tls_check_stored_hostname (certdata, hostname))
589       certerr_hostname = 1;
590   }
591   
592   /* see whether certificate is in our cache (certificates file) */
593   if (tls_compare_certificates (certdata))
594   {
595     if (certstat & GNUTLS_CERT_INVALID)
596     {
597       /* doesn't matter - have decided is valid because server
598        certificate is in our trusted cache */
599       certstat ^= GNUTLS_CERT_INVALID;
600     }
601     
602     if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
603     {
604       /* doesn't matter that we haven't found the signer, since
605        certificate is in our trusted cache */
606       certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
607     }
608     
609     if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
610     {
611       /* Hmm. Not really sure how to handle this, but let's say
612        that we don't care if the CA certificate hasn't got the
613        correct X.509 basic constraints if server certificate is
614        in our cache. */
615       certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
616     }
617   }
618
619   if (certstat & GNUTLS_CERT_REVOKED)
620   {
621     certerr_revoked = 1;
622     certstat ^= GNUTLS_CERT_REVOKED;
623   }
624   
625   if (certstat & GNUTLS_CERT_INVALID)
626   {
627     certerr_nottrusted = 1;
628     certstat ^= GNUTLS_CERT_INVALID;
629   }
630   
631   if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
632   {
633     /* NB: already cleared if cert in cache */
634     certerr_nottrusted = 1;
635     certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
636   }
637   
638   if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
639   {
640     /* NB: already cleared if cert in cache */
641     certerr_signernotca = 1;
642     certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
643   }
644
645   /* OK if signed by (or is) a trusted certificate */
646   /* we've been zeroing the interesting bits in certstat - 
647    don't return OK if there are any unhandled bits we don't
648    understand */
649   if (!(certerr_expired || certerr_notyetvalid || 
650         certerr_hostname || certerr_nottrusted) && certstat == 0)
651   {
652     gnutls_x509_crt_deinit (cert);
653     return 1;
654   }
655
656   /* interactive check from user */
657   menu = mutt_new_menu (-1);
658   menu->max = 25;
659   menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
660   for (i = 0; i < menu->max; i++)
661     menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
662   
663   row = 0;
664   strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
665   row++;
666   
667   buflen = sizeof (dn_common_name);
668   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
669                                      dn_common_name, &buflen) != 0)
670     dn_common_name[0] = '\0';
671   buflen = sizeof (dn_email);
672   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
673                                      dn_email, &buflen) != 0)
674     dn_email[0] = '\0';
675   buflen = sizeof (dn_organization);
676   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
677                                      dn_organization, &buflen) != 0)
678     dn_organization[0] = '\0';
679   buflen = sizeof (dn_organizational_unit);
680   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
681                                      dn_organizational_unit, &buflen) != 0)
682     dn_organizational_unit[0] = '\0';
683   buflen = sizeof (dn_locality);
684   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
685                                      dn_locality, &buflen) != 0)
686     dn_locality[0] = '\0';
687   buflen = sizeof (dn_province);
688   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
689                                      dn_province, &buflen) != 0)
690     dn_province[0] = '\0';
691   buflen = sizeof (dn_country);
692   if (gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
693                                      dn_country, &buflen) != 0)
694     dn_country[0] = '\0';
695   
696   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
697   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
698   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
699   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
700             dn_locality, dn_province, dn_country);
701   row++;
702   
703   strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING);
704   row++;
705   
706   buflen = sizeof (dn_common_name);
707   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
708                                             dn_common_name, &buflen) != 0)
709     dn_common_name[0] = '\0';
710   buflen = sizeof (dn_email);
711   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
712                                             dn_email, &buflen) != 0)
713     dn_email[0] = '\0';
714   buflen = sizeof (dn_organization);
715   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
716                                             dn_organization, &buflen) != 0)
717     dn_organization[0] = '\0';
718   buflen = sizeof (dn_organizational_unit);
719   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
720                                             dn_organizational_unit, &buflen) != 0)
721     dn_organizational_unit[0] = '\0';
722   buflen = sizeof (dn_locality);
723   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
724                                             dn_locality, &buflen) != 0)
725     dn_locality[0] = '\0';
726   buflen = sizeof (dn_province);
727   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
728                                             dn_province, &buflen) != 0)
729     dn_province[0] = '\0';
730   buflen = sizeof (dn_country);
731   if (gnutls_x509_crt_get_issuer_dn_by_oid (cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
732                                             dn_country, &buflen) != 0)
733     dn_country[0] = '\0';
734   
735   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
736   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
737   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
738   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
739             dn_locality, dn_province, dn_country);
740   row++;
741   
742   snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
743   
744   t = gnutls_x509_crt_get_activation_time (cert);
745   snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
746             tls_make_date (t, datestr, 30));
747   
748   t = gnutls_x509_crt_get_expiration_time (cert);
749   snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
750             tls_make_date (t, datestr, 30));
751   
752   fpbuf[0] = '\0';
753   tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), certdata);
754   snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf);
755   fpbuf[0] = '\0';
756   tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), certdata);
757   snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf);
758   
759   if (certerr_notyetvalid)
760   {
761     row++;
762     strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
763   }
764   if (certerr_expired)
765   {
766     row++;
767     strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING);
768   }
769   if (certerr_revoked)
770   {
771     row++;
772     strfcpy (menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING);
773   }
774   if (certerr_hostname)
775   {
776     row++;
777     strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING);
778   }
779   if (certerr_signernotca)
780   {
781     row++;
782     strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING);
783   }
784
785   snprintf (title, sizeof (title),
786             _("SSL Certificate check (certificate %d of %d in chain)"),
787             len - idx, len);
788   menu->title = title;
789   /* certificates with bad dates, or that are revoked, must be
790    accepted manually each and every time */
791   if (SslCertFile && !certerr_expired && !certerr_notyetvalid && !certerr_revoked)
792   {
793     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
794     menu->keys = _("roa");
795   }
796   else
797   {
798     menu->prompt = _("(r)eject, accept (o)nce");
799     menu->keys = _("ro");
800   }
801   
802   helpstr[0] = '\0';
803   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
804   safe_strcat (helpstr, sizeof (helpstr), buf);
805   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
806   safe_strcat (helpstr, sizeof (helpstr), buf);
807   menu->help = helpstr;
808   
809   done = 0;
810   set_option (OPTUNBUFFEREDINPUT);
811   while (!done)
812   {
813     switch (mutt_menuLoop (menu))
814     {
815       case -1:                  /* abort */
816       case OP_MAX + 1:          /* reject */
817       case OP_EXIT:
818         done = 1;
819         break;
820       case OP_MAX + 3:          /* accept always */
821         done = 0;
822         if ((fp = fopen (SslCertFile, "a")))
823         {
824           /* save hostname if necessary */
825           if (certerr_hostname)
826           {
827             fprintf(fp, "#H %s %s\n", hostname, fpbuf);
828             done = 1;
829           }
830           if (certerr_nottrusted)
831           {
832             done = 0;
833             ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", certdata,
834                                                   &pemdata);
835             if (ret == 0)
836             {
837               if (fwrite (pemdata.data, pemdata.size, 1, fp) == 1)
838               {
839                 done = 1;
840               }
841               gnutls_free (pemdata.data);
842             }
843           }
844           fclose (fp);
845         }
846         if (!done)
847         {
848           mutt_error (_("Warning: Couldn't save certificate"));
849           mutt_sleep (2);
850         }
851         else
852         {
853           mutt_message (_("Certificate saved"));
854           mutt_sleep (0);
855         }
856         /* fall through */
857       case OP_MAX + 2:          /* accept once */
858         done = 2;
859         break;
860     }
861   }
862   unset_option (OPTUNBUFFEREDINPUT);
863   mutt_menuDestroy (&menu);
864   gnutls_x509_crt_deinit (cert);
865
866   return (done == 2);
867 }
868
869 static int tls_check_certificate (CONNECTION* conn)
870 {
871   tlssockdata *data = conn->sockdata;
872   gnutls_session state = data->state;
873   const gnutls_datum *cert_list;
874   unsigned int cert_list_size = 0;
875   gnutls_certificate_status certstat;
876   int i, rc;
877
878   if (gnutls_auth_get_type (state) != GNUTLS_CRD_CERTIFICATE)
879   {
880     mutt_error (_("Unable to get certificate from peer"));
881     mutt_sleep (2);
882     return 0;
883   }
884
885   certstat = gnutls_certificate_verify_peers (state);
886
887   if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND)
888   {
889     mutt_error (_("Unable to get certificate from peer"));
890     mutt_sleep (2);
891     return 0;
892   }
893   if (certstat < 0)
894   {
895     mutt_error (_("Certificate verification error (%s)"),
896                 gnutls_strerror (certstat));
897     mutt_sleep (2);
898     return 0;
899   }
900
901   /* We only support X.509 certificates (not OpenPGP) at the moment */
902   if (gnutls_certificate_type_get (state) != GNUTLS_CRT_X509)
903   {
904     mutt_error (_("Certificate is not X.509"));
905     mutt_sleep (2);
906     return 0;
907   }
908
909   cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
910   if (!cert_list)
911   {
912     mutt_error (_("Unable to get certificate from peer"));
913     mutt_sleep (2);
914     return 0;
915   }
916
917   for (i = cert_list_size - 1; i >= 0; i--)
918   {
919     rc = tls_check_one_certificate (&cert_list[i], certstat, conn->account.host,
920                                     i, cert_list_size);
921     if (rc)
922       return rc;
923   }
924
925   return 0;
926 }