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