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