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