Andreas Krennmair:
[apps/madmutt.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., 59 Temple Place - Suite 330, Boston, MA  02111, 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_gnutls_socket_setup (CONNECTION* conn)
76 {
77   if (tls_init() < 0)
78     return -1;
79
80   conn->open    = tls_socket_open;
81   conn->read    = tls_socket_read;
82   conn->write   = tls_socket_write;
83   conn->close   = tls_socket_close;
84
85   return 0;
86 }
87
88 static int tls_socket_read (CONNECTION* conn, char* buf, size_t len)
89 {
90   tlssockdata *data = conn->sockdata;
91   int ret;
92
93   if (!data)
94   {
95     mutt_error ("Error: no TLS socket open");
96     mutt_sleep (2);
97     return -1;
98   }
99
100   ret = gnutls_record_recv (data->state, buf, len);
101   if (gnutls_error_is_fatal(ret) == 1)
102   {
103     mutt_error ("tls_socket_read (%s)", gnutls_strerror (ret));
104     mutt_sleep (4);
105     return -1;
106   }
107   return ret;
108 }
109
110 static int tls_socket_write (CONNECTION* conn, const char* buf, size_t len)
111 {
112   tlssockdata *data = conn->sockdata;
113   int ret;
114
115   if (!data)
116   {
117     mutt_error ("Error: no TLS socket open");
118     mutt_sleep (2);
119     return -1;
120   }
121
122   ret = gnutls_record_send (data->state, buf, len);
123   if (gnutls_error_is_fatal(ret) == 1)
124   {
125     mutt_error ("tls_socket_write (%s)", gnutls_strerror (ret));
126     mutt_sleep (4);
127     return -1;
128   }
129   return ret;
130 }
131
132 static int tls_socket_open (CONNECTION* conn)
133 {
134   if (raw_socket_open (conn) < 0)
135     return -1;
136
137   if (tls_negotiate (conn) < 0)
138   {
139     tls_socket_close (conn);
140     return -1;
141   }
142
143   return 0;
144 }
145
146 int mutt_gnutls_starttls (CONNECTION* conn)
147 {
148   if (tls_init() < 0)
149     return -1;
150
151   if (tls_negotiate (conn) < 0)
152     return -1;
153
154   conn->read    = tls_socket_read;
155   conn->write   = tls_socket_write;
156   conn->close   = tls_starttls_close;
157
158   return 0;
159 }
160
161 static int protocol_priority[] = {GNUTLS_TLS1, GNUTLS_SSL3, 0};
162
163 /* tls_negotiate: After TLS state has been initialised, attempt to negotiate
164  *   TLS over the wire, including certificate checks. */
165 static int tls_negotiate (CONNECTION * conn)
166 {
167   tlssockdata *data;
168   int err;
169
170   data = (tlssockdata *) safe_calloc (1, sizeof (tlssockdata));
171   conn->sockdata = data;
172   err = gnutls_certificate_allocate_credentials (&data->xcred);
173   if (err < 0)
174   {
175     FREE(&conn->sockdata);
176     mutt_error ("gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
177     mutt_sleep (2);
178     return -1;
179   }
180
181   gnutls_certificate_set_x509_trust_file (data->xcred, SslCertFile,
182                                           GNUTLS_X509_FMT_PEM);
183   /* ignore errors, maybe file doesn't exist yet */
184
185   if (SslCACertFile)
186   {
187     gnutls_certificate_set_x509_trust_file (data->xcred, SslCACertFile,
188                                             GNUTLS_X509_FMT_PEM);
189   }
190
191 /*
192   gnutls_set_x509_client_key (data->xcred, "", "");
193   gnutls_set_x509_cert_callback (data->xcred, cert_callback);
194 */
195
196   gnutls_init(&data->state, GNUTLS_CLIENT);
197
198   /* set socket */
199   gnutls_transport_set_ptr (data->state, (gnutls_transport_ptr)conn->fd);
200
201   /* disable TLS/SSL protocols as needed */
202   if (!option(OPTTLSV1) && !option(OPTSSLV3))
203   {
204     mutt_error (_("All available protocols for TLS/SSL connection disabled"));
205     goto fail;
206   }
207   else if (!option(OPTTLSV1))
208   {
209     protocol_priority[0] = GNUTLS_SSL3;
210     protocol_priority[1] = 0;
211   }
212   else if (!option(OPTSSLV3))
213   {
214     protocol_priority[0] = GNUTLS_TLS1;
215     protocol_priority[1] = 0;
216   }
217   /*
218   else
219     use the list set above
220   */
221
222   /* We use default priorities (see gnutls documentation),
223      except for protocol version */
224   gnutls_set_default_priority (data->state);
225   gnutls_protocol_set_priority (data->state, protocol_priority);
226
227   if (SslDHPrimeBits > 0)
228   {
229     gnutls_dh_set_prime_bits (data->state, SslDHPrimeBits);
230   }
231
232 /*
233   gnutls_set_cred (data->state, GNUTLS_ANON, NULL);
234 */
235
236   gnutls_credentials_set (data->state, GNUTLS_CRD_CERTIFICATE, data->xcred);
237
238   err = gnutls_handshake(data->state);
239
240   while (err == GNUTLS_E_AGAIN || err == GNUTLS_E_INTERRUPTED)
241   {
242     err = gnutls_handshake(data->state);
243   }
244   if (err < 0) {
245     if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
246     {
247       mutt_error("gnutls_handshake: %s(%s)", gnutls_strerror(err),
248                  gnutls_alert_get_name(gnutls_alert_get(data->state)));
249     }
250     else
251     {
252       mutt_error("gnutls_handshake: %s", gnutls_strerror(err));
253     }
254     mutt_sleep (2);
255     goto fail;
256   }
257
258   if (!tls_check_certificate(conn))
259     goto fail;
260
261   /* set Security Strength Factor (SSF) for SASL */
262   /* NB: gnutls_cipher_get_key_size() returns key length in bytes */
263   conn->ssf = gnutls_cipher_get_key_size (gnutls_cipher_get (data->state)) * 8;
264
265   mutt_message (_("SSL/TLS connection using %s (%s/%s/%s)"),
266                 gnutls_protocol_get_name (gnutls_protocol_get_version (data->state)),
267                 gnutls_kx_get_name (gnutls_kx_get (data->state)),
268                 gnutls_cipher_get_name (gnutls_cipher_get (data->state)),
269                 gnutls_mac_get_name (gnutls_mac_get (data->state)));
270   mutt_sleep (0);
271
272   return 0;
273
274  fail:
275   gnutls_certificate_free_credentials (data->xcred);
276   gnutls_deinit (data->state);
277   FREE(&conn->sockdata);
278   return -1;
279 }
280
281 static int tls_socket_close (CONNECTION* conn)
282 {
283   tlssockdata *data = conn->sockdata;
284   if (data)
285   {
286     gnutls_bye (data->state, GNUTLS_SHUT_RDWR);
287
288     gnutls_certificate_free_credentials (data->xcred);
289     gnutls_deinit (data->state);
290     safe_free ((void **) &conn->sockdata);
291   }
292
293   return raw_socket_close (conn);
294 }
295
296 static int tls_starttls_close (CONNECTION* conn)
297 {
298   int rc;
299
300   rc = tls_socket_close (conn);
301   conn->read = raw_socket_read;
302   conn->write = raw_socket_write;
303   conn->close = raw_socket_close;
304
305   return rc;
306 }
307
308 #define CERT_SEP "-----BEGIN"
309
310 /* this bit is based on read_ca_file() in gnutls */
311 static int tls_compare_certificates (const gnutls_datum *peercert)
312 {
313   gnutls_datum cert;
314   unsigned char *ptr;
315   FILE *fd1;
316   int ret;
317   gnutls_datum b64_data;
318   unsigned char *b64_data_data;
319   struct stat filestat;
320
321   if (stat(SslCertFile, &filestat) == -1)
322     return 0;
323
324   b64_data.size = filestat.st_size+1;
325   b64_data_data = (unsigned char *) safe_calloc (1, b64_data.size);
326   b64_data_data[b64_data.size-1] = '\0';
327   b64_data.data = b64_data_data;
328
329   fd1 = fopen(SslCertFile, "r");
330   if (fd1 == NULL) {
331     return 0;
332   }
333
334   b64_data.size = fread(b64_data.data, 1, b64_data.size, fd1);
335   fclose(fd1);
336
337   do {
338     ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
339     if (ret != 0)
340     {
341       FREE (&b64_data_data);
342       return 0;
343     }
344
345     ptr = (unsigned char *)strstr(b64_data.data, CERT_SEP) + 1;
346     ptr = (unsigned char *)strstr(ptr, CERT_SEP);
347
348     b64_data.size = b64_data.size - (ptr - b64_data.data);
349     b64_data.data = ptr;
350
351     if (cert.size == peercert->size)
352     {
353       if (memcmp (cert.data, peercert->data, cert.size) == 0)
354       {
355         /* match found */
356         gnutls_free(cert.data);
357         FREE (&b64_data_data);
358         return 1;
359       }
360     }
361
362     gnutls_free(cert.data);
363   } while (ptr != NULL);
364
365   /* no match found */
366   FREE (&b64_data_data);
367   return 0;
368 }
369
370 static void tls_fingerprint (gnutls_digest_algorithm algo,
371                              char* s, int l, const gnutls_datum* data)
372 {
373   unsigned char md[36];
374   size_t n;
375   int j;
376
377   n = 36;
378
379   if (gnutls_fingerprint (algo, data, (char *)md, &n) < 0)
380   {
381     snprintf (s, l, _("[unable to calculate]"));
382   }
383   else
384   {
385     for (j = 0; j < (int) n; j++)
386     {
387       char ch[8];
388       snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : ""));
389       strncat (s, ch, l);
390     }
391     s[2*n+n/2-1] = '\0'; /* don't want trailing space */
392   }
393 }
394
395 static char *tls_make_date (time_t t, char *s, size_t len)
396 {
397   struct tm *l = gmtime (&t);
398
399   if (l)
400     snprintf (s, len,  "%s, %d %s %d %02d:%02d:%02d UTC",
401               Weekdays[l->tm_wday], l->tm_mday, Months[l->tm_mon],
402               l->tm_year + 1900, l->tm_hour, l->tm_min, l->tm_sec);
403   else
404     strfcpy (s, _("[invalid date]"), len);
405
406   return (s);
407 }
408
409 static int tls_check_stored_hostname (const gnutls_datum *cert,
410                                       const char *hostname)
411 {
412   char buf[80];
413   FILE *fp;
414   char *linestr = NULL;
415   size_t linestrsize;
416   int linenum = 0;
417   regex_t preg;
418   regmatch_t pmatch[3];
419
420   /* try checking against names stored in stored certs file */
421   if ((fp = fopen (SslCertFile, "r")))
422   {
423     if (regcomp(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$", REG_ICASE|REG_EXTENDED) != 0)
424     {
425        regfree(&preg);
426        return 0;
427     }
428
429     buf[0] = '\0';
430     tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
431     while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum)) != NULL)
432     {
433       if(linestr[0] == '#' && linestr[1] == 'H')
434       {
435         if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
436         {
437           linestr[pmatch[1].rm_eo] = '\0';
438           linestr[pmatch[2].rm_eo] = '\0';
439           if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
440               strcmp(linestr + pmatch[2].rm_so, buf) == 0)
441           {
442             regfree(&preg);
443             safe_free((void**)&linestr);
444             fclose(fp);
445             return 1;
446           }
447         }
448       }
449     }
450
451     regfree(&preg);
452     fclose(fp);
453   }
454
455   /* not found a matching name */
456   return 0;
457 }
458
459 static int tls_check_certificate (CONNECTION* conn)
460 {
461   tlssockdata *data = conn->sockdata;
462   gnutls_session state = data->state;
463   char helpstr[SHORT_STRING];
464   char buf[SHORT_STRING];
465   char fpbuf[SHORT_STRING];
466   size_t buflen;
467   char dn_common_name[SHORT_STRING];
468   char dn_email[SHORT_STRING];
469   char dn_organization[SHORT_STRING];
470   char dn_organizational_unit[SHORT_STRING];
471   char dn_locality[SHORT_STRING];
472   char dn_province[SHORT_STRING];
473   char dn_country[SHORT_STRING];
474   MUTTMENU *menu;
475   int done, row, i, ret;
476   FILE *fp;
477   gnutls_x509_dn dn;
478   time_t t;
479   const gnutls_datum *cert_list;
480   int cert_list_size = 0;
481   gnutls_certificate_status certstat;
482   char datestr[30];
483   gnutls_x509_crt cert;
484   gnutls_datum pemdata;
485   int certerr_expired = 0;
486   int certerr_notyetvalid = 0;
487   int certerr_hostname = 0;
488   int certerr_nottrusted = 0;
489   int certerr_revoked = 0;
490   int certerr_signernotca = 0;
491
492   if (gnutls_auth_get_type(state) != GNUTLS_CRD_CERTIFICATE)
493   {
494     mutt_error (_("Unable to get certificate from peer"));
495     mutt_sleep (2);
496     return 0;
497   }
498
499   certstat = gnutls_certificate_verify_peers(state);
500
501   if (certstat == GNUTLS_E_NO_CERTIFICATE_FOUND)
502   {
503     mutt_error (_("Unable to get certificate from peer"));
504     mutt_sleep (2);
505     return 0;
506   }
507   if (certstat < 0)
508   {
509     mutt_error (_("Certificate verification error (%s)"), gnutls_strerror(certstat));
510     mutt_sleep (2);
511     return 0;
512   }
513
514   /* We only support X.509 certificates (not OpenPGP) at the moment */
515   if (gnutls_certificate_type_get(state) != GNUTLS_CRT_X509)
516   {
517     mutt_error (_("Error certificate is not X.509"));
518     mutt_sleep (2);
519     return 0;
520   }
521
522   if (gnutls_x509_crt_init(&cert) < 0)
523   {
524     mutt_error (_("Error initialising gnutls certificate data"));
525     mutt_sleep (2);
526     return 0;
527   }
528
529   cert_list = gnutls_certificate_get_peers(state, &cert_list_size);
530   if (!cert_list)
531   {
532     mutt_error (_("Unable to get certificate from peer"));
533     mutt_sleep (2);
534     return 0;
535   }
536
537   /* FIXME: Currently only check first certificate in chain. */
538   if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
539   {
540     mutt_error (_("Error processing certificate data"));
541     mutt_sleep (2);
542     return 0;
543   }
544
545   if (gnutls_x509_crt_get_expiration_time(cert) < time(NULL))
546   {
547     certerr_expired = 1;
548   }
549
550   if (gnutls_x509_crt_get_activation_time(cert) > time(NULL))
551   {
552     certerr_notyetvalid = 1;
553   }
554
555   if (!gnutls_x509_crt_check_hostname(cert, conn->account.host) &&
556       !tls_check_stored_hostname (&cert_list[0], conn->account.host))
557   {
558     certerr_hostname = 1;
559   }
560
561   /* see whether certificate is in our cache (certificates file) */
562   if (tls_compare_certificates (&cert_list[0]))
563   {
564     if (certstat & GNUTLS_CERT_INVALID)
565     {
566       /* doesn't matter - have decided is valid because server
567          certificate is in our trusted cache */
568       certstat ^= GNUTLS_CERT_INVALID;
569     }
570
571     if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
572     {
573       /* doesn't matter that we haven't found the signer, since
574          certificate is in our trusted cache */
575       certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
576     }
577
578     if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
579     {
580       /* Hmm. Not really sure how to handle this, but let's say
581          that we don't care if the CA certificate hasn't got the
582          correct X.509 basic constraints if server certificate is
583          in our cache. */
584       certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
585     }
586
587   }
588
589   if (certstat & GNUTLS_CERT_REVOKED)
590   {
591     certerr_revoked = 1;
592     certstat ^= GNUTLS_CERT_REVOKED;
593   }
594
595   if (certstat & GNUTLS_CERT_INVALID)
596   {
597     certerr_nottrusted = 1;
598     certstat ^= GNUTLS_CERT_INVALID;
599   }
600
601   if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
602   {
603     /* NB: already cleared if cert in cache */
604     certerr_nottrusted = 1;
605     certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
606   }
607
608   if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
609   {
610     /* NB: already cleared if cert in cache */
611     certerr_signernotca = 1;
612     certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
613   }
614
615   /* OK if signed by (or is) a trusted certificate */
616   /* we've been zeroing the interesting bits in certstat - 
617      don't return OK if there are any unhandled bits we don't
618      understand */
619   if (!(certerr_expired || certerr_notyetvalid || 
620         certerr_hostname || certerr_nottrusted) && certstat == 0)
621   {
622     gnutls_x509_crt_deinit(cert);
623     return 1;
624   }
625
626
627   /* interactive check from user */
628   menu = mutt_new_menu ();
629   menu->max = 25;
630   menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *));
631   for (i = 0; i < menu->max; i++)
632     menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char));
633
634   row = 0;
635   strfcpy (menu->dialog[row], _("This certificate belongs to:"), SHORT_STRING);
636   row++;
637
638   buflen = sizeof(dn_common_name);
639   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
640                                     dn_common_name, &buflen) != 0)
641     dn_common_name[0] = '\0';
642   buflen = sizeof(dn_email);
643   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
644                                     dn_email, &buflen) != 0)
645     dn_email[0] = '\0';
646   buflen = sizeof(dn_organization);
647   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
648                                     dn_organization, &buflen) != 0)
649     dn_organization[0] = '\0';
650   buflen = sizeof(dn_organizational_unit);
651   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
652                                     dn_organizational_unit, &buflen) != 0)
653     dn_organizational_unit[0] = '\0';
654   buflen = sizeof(dn_locality);
655   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
656                                     dn_locality, &buflen) != 0)
657     dn_locality[0] = '\0';
658   buflen = sizeof(dn_province);
659   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
660                                     dn_province, &buflen) != 0)
661     dn_province[0] = '\0';
662   buflen = sizeof(dn_country);
663   if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
664                                     dn_country, &buflen) != 0)
665     dn_country[0] = '\0';
666
667   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
668   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
669   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
670   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
671             dn_locality, dn_province, dn_country);
672   row++;
673   
674   strfcpy (menu->dialog[row], _("This certificate was issued by:"), SHORT_STRING);
675   row++;
676
677   buflen = sizeof(dn_common_name);
678   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
679                                     dn_common_name, &buflen) != 0)
680     dn_common_name[0] = '\0';
681   buflen = sizeof(dn_email);
682   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
683                                     dn_email, &buflen) != 0)
684     dn_email[0] = '\0';
685   buflen = sizeof(dn_organization);
686   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0,
687                                     dn_organization, &buflen) != 0)
688     dn_organization[0] = '\0';
689   buflen = sizeof(dn_organizational_unit);
690   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0,
691                                     dn_organizational_unit, &buflen) != 0)
692     dn_organizational_unit[0] = '\0';
693   buflen = sizeof(dn_locality);
694   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
695                                     dn_locality, &buflen) != 0)
696     dn_locality[0] = '\0';
697   buflen = sizeof(dn_province);
698   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0,
699                                     dn_province, &buflen) != 0)
700     dn_province[0] = '\0';
701   buflen = sizeof(dn_country);
702   if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
703                                     dn_country, &buflen) != 0)
704     dn_country[0] = '\0';
705
706   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s", dn_common_name, dn_email);
707   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organization);
708   snprintf (menu->dialog[row++], SHORT_STRING, "   %s", dn_organizational_unit);
709   snprintf (menu->dialog[row++], SHORT_STRING, "   %s  %s  %s",
710             dn_locality, dn_province, dn_country);
711   row++;
712
713   snprintf (menu->dialog[row++], SHORT_STRING, _("This certificate is valid"));
714
715   t = gnutls_x509_crt_get_activation_time(cert);
716   snprintf (menu->dialog[row++], SHORT_STRING, _("   from %s"), 
717             tls_make_date(t, datestr, 30));
718
719   t = gnutls_x509_crt_get_expiration_time(cert);
720   snprintf (menu->dialog[row++], SHORT_STRING, _("     to %s"), 
721             tls_make_date(t, datestr, 30));
722
723   fpbuf[0] = '\0';
724   tls_fingerprint (GNUTLS_DIG_SHA, fpbuf, sizeof (fpbuf), &cert_list[0]);
725   snprintf (menu->dialog[row++], SHORT_STRING, _("SHA1 Fingerprint: %s"), fpbuf);
726   fpbuf[0] = '\0';
727   tls_fingerprint (GNUTLS_DIG_MD5, fpbuf, sizeof (fpbuf), &cert_list[0]);
728   snprintf (menu->dialog[row++], SHORT_STRING, _("MD5 Fingerprint: %s"), fpbuf);
729
730   if (certerr_notyetvalid)
731   {
732     row++;
733     strfcpy (menu->dialog[row], _("WARNING: Server certificate is not yet valid"), SHORT_STRING);
734   }
735   if (certerr_expired)
736   {
737     row++;
738     strfcpy (menu->dialog[row], _("WARNING: Server certificate has expired"), SHORT_STRING);
739   }
740   if (certerr_revoked)
741   {
742     row++;
743     strfcpy (menu->dialog[row], _("WARNING: Server certificate has been revoked"), SHORT_STRING);
744   }
745   if (certerr_hostname)
746   {
747     row++;
748     strfcpy (menu->dialog[row], _("WARNING: Server hostname does not match certificate"), SHORT_STRING);
749   }
750   if (certerr_signernotca)
751   {
752     row++;
753     strfcpy (menu->dialog[row], _("WARNING: Signer of server certificate is not a CA"), SHORT_STRING);
754   }
755
756   menu->title = _("TLS/SSL Certificate check");
757   /* certificates with bad dates, or that are revoked, must be
758      accepted manually each and every time */
759   if (SslCertFile && !certerr_expired && !certerr_notyetvalid && !certerr_revoked)
760   {
761     menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
762     menu->keys = _("roa");
763   }
764   else
765   {
766     menu->prompt = _("(r)eject, accept (o)nce");
767     menu->keys = _("ro");
768   }
769   
770   helpstr[0] = '\0';
771   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
772   strncat (helpstr, buf, sizeof (helpstr));
773   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP);
774   strncat (helpstr, buf, sizeof (helpstr));
775   menu->help = helpstr;
776
777   done = 0;
778   while (!done)
779   {
780     switch (mutt_menuLoop (menu))
781     {
782       case -1:                  /* abort */
783       case OP_MAX + 1:          /* reject */
784       case OP_EXIT:
785         done = 1;
786         break;
787       case OP_MAX + 3:          /* accept always */
788         done = 0;
789         if ((fp = fopen (SslCertFile, "a")))
790         {
791           /* save hostname if necessary */
792           if (certerr_hostname)
793           {
794             fprintf(fp, "#H %s %s\n", conn->account.host, fpbuf);
795             done = 1;
796           }
797           if (certerr_nottrusted)
798           {
799             done = 0;
800             ret = gnutls_pem_base64_encode_alloc ("CERTIFICATE", &cert_list[0],
801                                                   &pemdata);
802             if (ret == 0)
803             {
804               if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
805               {
806                 done = 1;
807               }
808               gnutls_free(pemdata.data);
809             }
810           }
811           fclose (fp);
812         }
813         if (!done)
814         {
815           mutt_error (_("Warning: Couldn't save certificate"));
816           mutt_sleep (2);
817         }
818         else
819         {
820           mutt_message (_("Certificate saved"));
821           mutt_sleep (0);
822         }
823         /* fall through */
824       case OP_MAX + 2:          /* accept once */
825         done = 2;
826         break;
827     }
828   }
829   mutt_menuDestroy (&menu);
830   gnutls_x509_crt_deinit(cert);
831   return (done == 2);
832 }