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